belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial: The Definitive Guide belaran@999: belaran@999: belaran@999: Compiled from $rev_id$ belaran@999: belaran@999: 1 belaran@999: 9780596800673 belaran@999: belaran@999: belaran@999: Bryan belaran@999: O'Sullivan belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mike belaran@999: Loukides belaran@999: belaran@999: belaran@999: belaran@999: 2006 belaran@999: 2007 belaran@999: 2008 belaran@999: 2009 belaran@999: Bryan O'Sullivan belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Preface belaran@999: belaran@999: belaran@999: Un conte technique belaran@999: belaran@999: Il y a quelques années, quand j'ai voulu expliqué belaran@999: pourquoi je pensais que le gestion de révision distribuée est importante, belaran@999: le domaine était encore si nouveau qu'il n'y avait presque aucune belaran@999: littérature publiée pour servir de référence aux personnes intéressées. belaran@999: belaran@999: Bien qu'à cette époque je passais beaucoup de temps belaran@999: à travailler sur les entrailles de Mercurial, je me suis mis à la belaran@999: rédaction de ce livre parce qu'il me semblait la manière la plus efficace belaran@999: d'aider notre logiciel à atteindre un vaste auditoire, toujours avec belaran@999: l'idée que la gestion de révision devrait être distribuée par nature. J'ai belaran@999: publié ce libre en ligne sous une licence libre pour la même raison : pour belaran@999: diffuser la parole auprès du monde. belaran@999: belaran@999: Il y a un rythme familier à un bon livre sur un logiciel belaran@999: qui ressemble de près au fait de conter une histoire : Pourquoi ceci est ? belaran@999: Pourquoi ceci est important ? Comment peut il m'aider ? Comment m'en belaran@999: servir ? Dans ce livre, j'essaye de répondre à toutes ces questions pour belaran@999: la gestion de révision distribuée en général, et pour Mercurial en belaran@999: particulier. belaran@999: belaran@999: belaran@999: belaran@999: Merci de votre soutien à Mercurial belaran@999: belaran@999: En achetant une copie de ce livre, vous soutenez le belaran@999: développement et la liberté de Mercurial en particulier, et dans belaran@999: l'Open Source, au logiciel libre en général. O'Reilly Media et belaran@999: moi-même donnons les revenus issus des ventes de ce livre à la belaran@999: Software Freedom Conservancy (http://www.softwarefreedom.org/) belaran@999: qui fournit un support juridique à Mercurial et à de belaran@999: nombreux autres projets Open Source proéminents et de qualité. belaran@999: belaran@999: belaran@999: belaran@999: Remerciements belaran@999: belaran@999: Ce livre n'aurait pas vu le jour sans les belaran@999: efforts de Matt Mackal, l'auteur et le chef du projet Mercurial. belaran@999: Il est assisté très efficacement par des centaines de contributeurs belaran@999: volontaires à travers le monde. belaran@999: belaran@999: Les enfants, Cian et Ruairi, ont toujours été prêt belaran@999: à m'aider à me reposer avec de merveilleux et impulsif jeux d'enfants. belaran@999: Je tiens aussi à remercier mon ex-femme, Shannon, pour son soutien. belaran@999: belaran@999: belaran@999: Mes collègues et amis m'ont aidé et assisté de belaran@999: de nombreuses manières. Cette liste de personne est nécessaire mais très belaran@999: incomplète : Stephen Hahn, Karyn Ritter, Bonnie Corwin, James Vasile, belaran@999: Matt Norwood, Eben Moglen, Bradley Kuhn, Robert Walsh, Jeremy belaran@999: Fitzhardinge, Rachel Chalmers. belaran@999: belaran@999: J'ai conçu ce livre de manière ouverte, en publiant belaran@999: des brouillons des chapitres du livre sur des site web, au fur et à belaran@999: mesure que je les réalisais. Leurs lecteurs m'ont fait des retours belaran@999: utilisant l'application web que j'avais développée. A la fin de sa belaran@999: conception, plus de 100 personnes m'avaient fait des commentaires, belaran@999: un chiffre incroyable quand l'on considère que ce système de belaran@999: commentaire n'a tourné que dans les deux derniers mois de la belaran@999: rédaction du livre. belaran@999: belaran@999: J'aimerais particulièrement remercier les belaran@999: personnes suivantes, dont les commentaires représentent plus belaran@999: d'un tiers de l'ensemble de ces derniers. Je voudrais les belaran@999: remercier pour leur attention et effort à me faire des retours belaran@999: très détaillés. belaran@999: belaran@999: Martin Geisler, Damien Cassou, Alexey Bakhirkin, Till Plewe, belaran@999: Dan Himes, Paul Sargent, Gokberk Hamurcu, Matthijs van der belaran@999: Vleuten, Michael Chermside, John Mulligan, Jordi Fita, Jon belaran@999: Parise. belaran@999: belaran@999: Je souhaite aussi remercier l'aide des personnes belaran@999: qui ont découvert des erreurs et fournit des suggestions avisées belaran@999: à travers tout le livre. belaran@999: belaran@999: Jeremy W. Sherman, Brian Mearns, Vincent Furia, Iwan belaran@999: Luijks, Billy Edwards, Andreas Sliwka, Paweł Sołyga, Eric belaran@999: Hanchrow, Steve Nicolai, Michał Masłowski, Kevin Fitch, Johan belaran@999: Holmberg, Hal Wine, Volker Simonis, Thomas P Jakobsen, Ted belaran@999: Stresen-Reuter, Stephen Rasku, Raphael Das Gupta, Ned belaran@999: Batchelder, Lou Keeble, Li Linxiao, Kao Cardoso Félix, Joseph belaran@999: Wecker, Jon Prescot, Jon Maken, John Yeary, Jason Harris, belaran@999: Geoffrey Zheng, Fredrik Jonson, Ed Davies, David Zumbrunnen, belaran@999: David Mercer, David Cabana, Ben Karel, Alan Franzoni, Yousry belaran@999: Abdallah, Whitney Young, Vinay Sajip, Tom Towle, Tim Ottinger, belaran@999: Thomas Schraitle, Tero Saarni, Ted Mielczarek, Svetoslav belaran@999: Agafonkin, Shaun Rowland, Rocco Rutte, Polo-Francois Poli, belaran@999: Philip Jenvey, Petr Tesałék, Peter R. Annema, Paul Bonser, belaran@999: Olivier Scherler, Olivier Fournier, Nick Parker, Nick Fabry, belaran@999: Nicholas Guarracino, Mike Driscoll, Mike Coleman, Mietek Bák, belaran@999: Michael Maloney, László Nagy, Kent Johnson, Julio Nobrega, Jord belaran@999: Fita, Jonathan March, Jonas Nockert, Jim Tittsler, Jeduan belaran@999: Cornejo Legorreta, Jan Larres, James Murphy, Henri Wiechers, belaran@999: Hagen Möbius, Gábor Farkas, Fabien Engels, Evert Rol, Evan belaran@999: Willms, Eduardo Felipe Castegnaro, Dennis Decker Jensen, Deniz belaran@999: Dogan, David Smith, Daed Lee, Christine Slotty, Charles Merriam, belaran@999: Guillaume Catto, Brian Dorsey, Bob Nystrom, Benoit Boissinot, belaran@999: Avi Rosenschein, Andrew Watts, Andrew Donkin, Alexey Rodriguez, belaran@999: Ahmed Chaudhary. belaran@999: belaran@999: belaran@999: belaran@999: Conventions utilisées dans ce livre belaran@999: belaran@999: Les conventions typographiques suivantes sont utilisées dans ce livre : belaran@999: belaran@999: belaran@999: belaran@999: Italique belaran@999: belaran@999: belaran@999: Indique les termes nouveaux, les URLs, les belaran@999: adresses mail, les noms de fichiers et les extensions de belaran@999: fichier. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Taille constante belaran@999: belaran@999: belaran@999: Utilisé pour les extraits de code, comme belaran@999: dans les paragraphes pour référer aux éléments du programme, belaran@999: tels que les variables ou les noms de fonctions, de bases belaran@999: de données, de types de données, de variables d'environnement, belaran@999: d'instructions, et de mots clés. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Taille constante avec gras belaran@999: belaran@999: belaran@999: Afficher les commandes ou autres textes qui belaran@999: devraient être saisis par l'utilisateur. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Constante avec italique belaran@999: belaran@999: belaran@999: Affiche les textes qui devraient être remplacés belaran@999: par une valeur définie par l'utilisateur ou des valeurs définies belaran@999: selon le contexte. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Cette icône indique une astuce, une suggestion ou belaran@999: une note d'ordre général. belaran@999: belaran@999: belaran@999: belaran@999: Cette icône est un message d'alerte ou de prudence. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Utiliser les exemples de code belaran@999: belaran@999: Ce livre est ici pour vous aider dans votre belaran@999: travail. De manière générale, vous pouvez donc utiliser le code belaran@999: de ce livre dans vos programmes et votre documentation. Vous belaran@999: n'avez pas à nous contacter pour nous demander la permission belaran@999: de le faire, à moins que vous ne reproduisiez une partie significative belaran@999: du code. Par exemple, écrire un programme qui utilise plusieurs belaran@999: extraits de code du livre ne demande aucune autorisation particulière. belaran@999: Vendre ou distribuer un CD-ROM provenant des livres O'Reilly demande belaran@999: à l'inverse une autorisation. Répondre à une question en citant ce belaran@999: livre ou ses exemples de code ne demande aucune autorisation préalable. belaran@999: Intégrer une grande quantité des codes d'exemples de ce livre dans belaran@999: votre propre ouvrage demande une autorisation de notre part. belaran@999: belaran@999: Nous apprécions, sans l'exiger, que vous citiez belaran@999: l'ouvrage dans vos écrits l'utilisant, en indiquant le titre, belaran@999: l'auteur, l'éditeur et son ISBN. Par exemple: “Titre du belaran@999: livre par Son Auteur. Copyright 2008 O’Reilly Media, Inc., belaran@999: 978-0-596-xxxx-x.” belaran@999: belaran@999: Si vous estimez que votre usage des exemples de code belaran@999: dépasse le cadre défini ci dessus, n'hésitez pas à nous contacter : belaran@999: permissions@oreilly.com. belaran@999: belaran@999: belaran@999: belaran@999: Safari® Books Online belaran@999: belaran@999: belaran@999: Quand vous voyez l'icône de Safari® Books Online belaran@999: sur la couverture d'un de vos livres techniques préférés, cela signifie belaran@999: que le livre est disponible, en ligne, à travers le O’Reilly Network Safari belaran@999: Bookshelf. belaran@999: belaran@999: belaran@999: Safari offre une solution qui est meilleure que belaran@999: les e-books. C'est une bibliothèque virtuelle qui vous laisse belaran@999: aisément rechercher dans des milliers de livres, mais aussi belaran@999: copier-coller leurs exemples, télécharger des chapitres, et belaran@999: trouver des réponses rapides quand vous avez besoin d'une belaran@999: information précise et à jour. Essayez le gratuitement : belaran@999: http://my.safaribooksonline.com. belaran@999: belaran@999: belaran@999: belaran@999: Comment nous contacter belaran@999: belaran@999: Merci d'adresser vos commentaires et vos questions belaran@999: sur ce livre à son éditeur: belaran@999: belaran@999: belaran@999: O’Reilly Media, Inc. belaran@999: belaran@999: 1005 Gravenstein Highway North belaran@999: belaran@999: Sebastopol, CA 95472 belaran@999: belaran@999: 800-998-9938 (in the United States or Canada) belaran@999: belaran@999: 707-829-0515 (international or local) belaran@999: belaran@999: 707 829-0104 (fax) belaran@999: belaran@999: belaran@999: Nous avons une page web pour cet ouvrage, où nous belaran@999: publions des errata, des exemples, et encore d'autres informations belaran@999: additionnelles. Vous pouvez accéder à cette page par l'URL suivante: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: N'oubliez pas de mettre à jour l'attribut <url> aussi. belaran@999: belaran@999: Pour commenter ou poser des questions techniques belaran@999: sur cet ouvrage, envoyez un email à : belaran@999: belaran@999: belaran@999: bookquestions@oreilly.com belaran@999: belaran@999: belaran@999: Pour plus d'informations sur nos livres, nos belaran@999: conférences, nos centres d'informations, et le réseau O’Reilly, belaran@999: voyez notre site web : belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Comment en est on arrivé là ? belaran@999: belaran@999: belaran@999: À propos de la gestion source belaran@999: belaran@999: La gestion de sources est un processus permettant de gérer différentes belaran@999: versions de la même information. Dans sa forme la plus simple, c'est belaran@999: ce que tout le monde fait manuellement : quand vous modifiez belaran@999: un fichier, vous le sauvegardez sous un nouveau nom contenant un numéro, belaran@999: à chaque fois plus grand que celui de la version précédente. belaran@999: belaran@999: Ce genre de gestion de version manuelle est cependant facilement sujette belaran@999: aux erreurs, ainsi, depuis longtemps, des logiciels existent pour belaran@999: résoudre cette problématique. Les premiers outils de gestion de sources belaran@999: étaient destinés à aider un seul utilisateur, à automatiser la gestion belaran@999: des versions d'un seul fichier. Dans les dernières décades, cette cible belaran@999: s'est largement agrandie, ils gèrent désormais de multiples fichiers, et belaran@999: aident un grand nombre de personnes à travailler ensemble. Les outils les belaran@999: plus modernes n'ont aucune difficulté à gérer plusieurs milliers de belaran@999: personnes travaillant ensemble sur des projets regroupant plusieurs belaran@999: centaines de milliers de fichiers. belaran@999: belaran@999: L'arrivée de la gestion de révision distribuée est belaran@999: relativement récente, et, pour le moment, ce nouveau domaine a grandi belaran@999: grâce à la volonté des gens d'explorer ces territoires encore inconnus. belaran@999: belaran@999: belaran@999: J'écris un livre sur la gestion de révision distribuée belaran@999: parce que je pense qu'il s'agit d'un sujet important qui mérite un guide belaran@999: du terrain. J'ai choisi d'écrire un livre sur Mercurial car il est belaran@999: l'outil le plus facile pour découvrir ce nouveau domaine, tout en étant belaran@999: un outil efficace qui répond aux demandes d'environnements réels et belaran@999: difficiles, là où d'autres outils de gestions de versions s'effondrent. belaran@999: belaran@999: belaran@999: Pourquoi utiliser un gestionnaire de source ? belaran@999: belaran@999: Il y a de nombreuses raisons pour que vous ou votre équipe souhaitiez belaran@999: utiliser un outil automatisant la gestion de version pour votre projet. belaran@999: belaran@999: belaran@999: L'outil se chargera de suivre l'évolution de votre projet, sans belaran@999: que vous ayez à le faire. Pour chaque modification, vous aurez à votre belaran@999: disposition un journal indiquant qui a fait quoi, pourquoi belaran@999: il l'a fait, quand il l'a fait, et belaran@999: ce qu'il a modifié. belaran@999: belaran@999: Quand vous travaillez avec d'autres personnes, les logiciels de belaran@999: gestion de source facilitent le travail collaboratif. Par exemple, quand belaran@999: plusieurs personnes font, plus ou moins simultanément, des modifications belaran@999: incompatibles, le logiciel vous aidera à identifier et à résoudre les conflits. belaran@999: belaran@999: L'outil vous aidera à réparer vos erreurs. Si vous effectuez un changement belaran@999: qui se révèle être une erreur, vous pourrez revenir à une version belaran@999: antérieure d'un fichier ou même d'un ensemble de fichiers. En fait, un outil de belaran@999: gestion de source vraiment efficace vous permettra d'identifier à quel belaran@999: moment le problème est apparu (voir la section pour plus belaran@999: de détails). belaran@999: belaran@999: L'outil vous permettra aussi de travailler sur plusieurs versions différentes belaran@999: de votre projet et de gérer l'écart entre chacune. belaran@999: belaran@999: La plupart de ces raisons ont autant d'importances —du belaran@999: moins en théorie— que vous travailliez sur un projet pour vous, ou belaran@999: avec une centaine d'autres personnes. belaran@999: belaran@999: belaran@999: Une question fondamentale à propos des outils de gestion de belaran@999: source, qu'il s'agisse du projet d'une personne ou d'une grande équipe, est belaran@999: quels sont ses avantages par rapport à ses belaran@999: coûts. Un outil qui est difficile à utiliser ou à belaran@999: comprendre exigera un lourd effort d'adaptation. belaran@999: belaran@999: belaran@999: )Un projet de cinq milles personnes s'effondrera très belaran@999: certainement de lui même sans aucun processus et outil de gestion de belaran@999: source. Dans ce cas, le coût d'utilisation d'un logiciel de gestion de belaran@999: source est dérisoire puisque sans, l'échec est presque belaran@999: garanti. belaran@999: belaran@999: belaran@999: D'un autre coté, un rapide hack d'une personne belaran@999: peut sembler un contexte bien pauvre pour utiliser un outil de gestion de belaran@999: source, car, bien évidement le coût d'utilisation dépasse le coût total du belaran@999: projet. N'est ce pas ? belaran@999: belaran@999: belaran@999: Mercurial supporte ces deux belaran@999: échelles de travail. Vous pouvez apprendre les bases en quelques belaran@999: minutes seulement, et, grâce à sa performance, vous pouvez l'utiliser belaran@999: avec facilité sur le plus petit des projets. Cette simplicité belaran@999: signifie que vous n'avez pas de concept obscurs ou de séquence de belaran@999: commandes défiant l'imagination, sans aucune corrélation avec belaran@999: ce que vous êtes entrain de faire. En même belaran@999: temps, ces mêmes performances et sa nature belaran@999: peer-to-peer vous permettent d'adapter, sans belaran@999: difficulté, son utilisation à de très grands projets. belaran@999: belaran@999: belaran@999: Aucun outil de gestion de source ne peut sauver un belaran@999: projet mal mené, mais un bon outil peut rendre beaucoup plus fluide belaran@999: votre travail. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Les multiples noms de la gestion de source belaran@999: belaran@999: La gestion de source belaran@999: belaran@999: est un domaine tellement large qu'il n'existe pas qu'un seul nom ou belaran@999: acronyme pour le désigner. Voici quelques noms ou acronymes que vous belaran@999: rencontrerez le plus souvent. belaran@999: belaran@999: belaran@999: belaran@999: : belaran@999: belaran@999: belaran@999: belaran@999: Revision control (RCS) belaran@999: Software configuration management (SCM), ou belaran@999: configuration management belaran@999: Source code management belaran@999: Source code control, ou source control belaran@999: Version control (VCS) belaran@999: belaran@999: Certaines personnes prétendent que ces termes ont en fait belaran@999: des sens différents mais en pratique ils se recouvrent tellement qu'il n'y belaran@999: a pas réellement de manière pertinente de les distinguer. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: A propos des exemples dans ce livre belaran@999: belaran@999: Ce livre prend une approche non usuel pour les exemples belaran@999: de code. Tous les exemples sont en live — Chacun belaran@999: est actuellement le résultat d'un script shell qui exécute les belaran@999: commandes Mercurial que vous voyez. A chaque fois qu'une image du livre belaran@999: est construite à partir des sources, tous les scripts d'exemple sont belaran@999: lancés automatiquement, et leurs résultats effectifs sont comparés aux belaran@999: résultats attendus. belaran@999: belaran@999: L'avantage de dette approche est que les exemples sont belaran@999: toujours précis ; ils décrivent exactement la belaran@999: conduite de la version de Mercurial qui est mentionnée en entête du belaran@999: livre. Si je met à jour la version de Mercurial que je suis en train de belaran@999: documenter, et que la sortie de certaines commandes change, la belaran@999: construction du livre échoue. belaran@999: belaran@999: belaran@999: Il existe un petit désavantage à cette approche qui est que les dates et belaran@999: heures que vous verrez dans les exemples tendent à être belaran@999: écrasés ensemble, dans le sens où elles ne sont pas belaran@999: celles qu'elles auraient été si un humain avait tapé les commandes. En belaran@999: effet, humain ne peut pas taper plus d'une commande toutes les quelques belaran@999: secondes, avec le temps qui s'écoule, mes scripts d'exemples exécutent belaran@999: plusieurs commandes en une seconde. belaran@999: belaran@999: belaran@999: Une circonstance de ceci est que plusieurs commits belaran@999: consécutifs dans un exemple peuvent apparaître comme ayant eu lieu belaran@999: durant la même seconde. belaran@999: Vous pouvez observer le phénomène dans l'exemple bisect dans belaran@999: belaran@999: belaran@999: Donc, lorsque vous lisez ces exemples, ne prêtez pas trop belaran@999: d'importance aux dates et heures que vous voyez dans la sortie des belaran@999: commandes. Cependant, soyez confiants que le belaran@999: comportement que vous voyez est consistent et reproductible belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Tendances de la gestion de source belaran@999: belaran@999: Il y a eu une tendance évidente dans le développement et belaran@999: l'utilisation d'outils de gestion de source depuis les quatre dernières belaran@999: décades, au fur et à mesure que les utilisateurs se sont habitués à belaran@999: leur outils et se sont sentis contraints par leurs limitations. belaran@999: belaran@999: belaran@999: La première génération commença simplement par gérer un belaran@999: fichier unique sur un ordinateur individuel. Cependant, même si ces belaran@999: outils présentaient une grande avancée par rapport à la gestion belaran@999: manuelle des versions, leur modèle de verrouillage et leur utilisation belaran@999: limitée à un seul ordinateur rendaient leur utilisation possible belaran@999: uniquement dans une très petite équipe. belaran@999: belaran@999: belaran@999: La seconde génération a assoupli ces contraintes en belaran@999: adoptant une architecture réseau et centralisée, permettant de gérer belaran@999: plusieurs projets entiers en même temps. Alors que les projets belaran@999: grandirent en taille, ils rencontrèrent de nouveaux problèmes. Avec les belaran@999: clients discutant régulièrement avec le serveurs, la montée en charge belaran@999: devint un réel problème sur les gros projets. Une connexion réseau peu belaran@999: fiable pouvait complètement empêcher les utilisateurs distants de belaran@999: dialoguer avec le serveur. Alors que les projets Open Source commencèrent à mettre en place des belaran@999: accès en lecture seule disponible anonymement, les utilisateurs sans belaran@999: les privilèges de commit réalisèrent qu'ils ne pouvaient belaran@999: pas utiliser les outils pour collaborer naturellement avec le projet, belaran@999: comme ils ne pouvaient pas non plus enregistrer leurs modifications. belaran@999: belaran@999: belaran@999: La génération actuelle des outils de gestion de source belaran@999: est peer-to-peer par nature. Tous ces systèmes ont belaran@999: abandonné la dépendance à un serveur central, et ont permis à leur belaran@999: utilisateur de distribuer les données de leur gestion de source à qui belaran@999: en a besoin. La collaboration à travers Internet a transformé la belaran@999: contrainte technologique en une simple question de choix et de belaran@999: consensus. Les outils modernes peuvent maintenant fonctionner en mode belaran@999: déconnecté sans limite et de manière autonome, la connexion au réseau belaran@999: n'étant nécessaire que pour synchroniser les modifications avec les belaran@999: autres dépôts. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Quelques avantages des gestionnaires de source distribués belaran@999: belaran@999: Même si les gestionnaire de source distribués sont depuis belaran@999: plusieurs années assez robustes et aussi utilisables que leurs belaran@999: prédécesseurs, les utilisateurs d'autres outils n'y ont pas encore été belaran@999: sensibilisés. Les gestionnaires de source distribués se distinguent belaran@999: particulièrement de leurs équivalents centralisés de nombreuses belaran@999: manières. belaran@999: belaran@999: belaran@999: Pour un développeur individuel, ils restent beaucoup plus belaran@999: rapides que les outils centralisés. Cela pour une raison simple : un belaran@999: outil centralisé doit toujours dialoguer à travers le réseau pour la belaran@999: plupart des opérations, car presque toutes les métadonnées sont belaran@999: stockées sur la seule copie du serveur central. Un outil distribué belaran@999: stocke toute ses métadonnées localement. À tâche égale, effectuer un belaran@999: échange avec le réseau ajoute un délai aux outils centralisés. Ne belaran@999: sous-estimez pas la valeur d'un outil rapide : vous allez passer belaran@999: beaucoup de temps à interagir avec un logiciel de gestion de source. belaran@999: belaran@999: belaran@999: Les outils distribués sont complètement indépendants des belaran@999: aléas de votre serveur, d'autant plus qu'ils répliquent les métadonnées belaran@999: à beaucoup d'endroits. Si votre serveur central prend feu, vous avez belaran@999: intérêt à ce que les médias de sauvegardes soient fiables, et que votre belaran@999: dernier backup soit récent et fonctionne sans problème. belaran@999: Avec un outil distribué, vous avez autant de backup que belaran@999: de contributeurs. belaran@999: belaran@999: belaran@999: En outre, la fiabilité de votre réseau affectera beaucoup belaran@999: moins les outils distribués. Vous ne pouvez même pas utiliser un outil belaran@999: centralisé sans connexion réseau, à l'exception de quelques commandes, belaran@999: très limitées. Avec un outil distribué, si votre connexion réseau tombe belaran@999: pendant que vous travaillez, vous pouvez ne même pas vous en rendre belaran@999: compte. La seule chose que vous ne serez pas capable de faire sera de belaran@999: communiquer avec des dépôts distants, opération somme toute assez rare belaran@999: en comparaison aux opérations locales. Si vous avez une équipe de belaran@999: collaborateurs très dispersée ceci peut être significatif. belaran@999: belaran@999: belaran@999: belaran@999: Avantages pour les projets Open Source belaran@999: belaran@999: Si vous prenez goût à un projet Open Source et que vous décidez de commencer belaran@999: à toucher à son code, et que le projet utilise un gestionnaire de belaran@999: source distribué, vous êtes immédiatement un "pair" avec les belaran@999: personnes formant le cœur du projet. S'ils publient belaran@999: leurs dépôts, vous pouvez immédiatement copier leurs historiques de belaran@999: projet, faire des modifications, enregistrer votre travail en belaran@999: utilisant les mêmes outils qu'eux. Par comparaison avec un outil belaran@999: centralisé, vous devez utiliser un logiciel en mode lecture belaran@999: seule à moins que quelqu'un ne vous donne les privilèges de belaran@999: commit sur le serveur central. Avant ça, vous ne serez belaran@999: pas capable d'enregistrer vos modifications, et vos propres belaran@999: modifications risqueront de se corrompre chaque fois que vous belaran@999: essayerez de mettre à jour à votre espace de travail avec le serveur belaran@999: central. belaran@999: belaran@999: belaran@999: belaran@999: Le non-problème du "fork" belaran@999: belaran@999: Il a été souvent suggéré que les gestionnaires de belaran@999: source distribués posent un risque pour les projets Open Source car ils facilitent grandement la belaran@999: création de fork. belaran@999: belaran@999: Un fork apparait quand il y des divergences d'opinion belaran@999: ou d'attitude au sein d'un groupe de développeurs qui aboutissent à belaran@999: la décision de ne plus travailler ensemble. Chaque parti s'empare belaran@999: d'une copie plus ou moins complète du code source du projet et belaran@999: continue dans sa propre direction. belaran@999: belaran@999: belaran@999: belaran@999: Parfois ces différents partis décident de se belaran@999: réconcilier. Avec un serveur central, l'aspect belaran@999: technique de cette réconciliation est un belaran@999: processus douloureux, et essentiellement manuel. Vous devez décider belaran@999: quelle modification est la gagnante, et replacer, par belaran@999: un moyen ou un autre, les modifications de l'autre équipe dans belaran@999: l'arborescence du projet. Ceci implique généralement la perte d'une belaran@999: partie de l'historique d'un des partis, ou même des deux. belaran@999: belaran@999: belaran@999: Ce que les outils distribués permettent à ce sujet est belaran@999: probablement la meilleure façon de développer un belaran@999: projet. Chaque modification que vous effectuez est potentiellement un belaran@999: fork. La grande force de cette approche est que les belaran@999: gestionnaires de source distribués doivent être vraiment très belaran@999: efficaces pour fusionner (merge) belaran@999: belaran@999: des forks, car les forks, dans ce belaran@999: contexte, arrivent tout le temps. belaran@999: belaran@999: belaran@999: Si chaque altération que n'importe qui effectue, à tout belaran@999: moment, est vue comme un fork à fusionner, alors ce belaran@999: que le monde de l'Open Source voit belaran@999: comme un fork devient uniquement belaran@999: une problématique sociale. En fait, les outils de gestions de source belaran@999: distribués réduisent les chances de belaran@999: fork : belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Ils éliminent la distinction sociale qu'imposent les outils belaran@999: centralisés entre les membres du projets (ceux qui ont accès au belaran@999: commit) et ceux de l'extérieur (ce qui ne l'ont belaran@999: pas). belaran@999: belaran@999: Ils rendent plus facile la réconciliation après un belaran@999: fork social, car tout ce qu'elle implique est une belaran@999: simple fusion. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Certaines personnes font de la résistance envers les belaran@999: gestionnaires de source distribués parce qu'ils veulent garder un belaran@999: contrôle ferme sur leur projet, et ils pensent que les outils belaran@999: centralisés leur fournissent ce contrôle. Néanmoins, si c'est votre belaran@999: cas, sachez que si vous publiez votre dépôt CVS ou Subversion de belaran@999: manière publique, il existe une quantité d'outils disponibles pour belaran@999: récupérer entièrement votre projet et son historique (quoique belaran@999: lentement) et le récréer ailleurs, sans votre contrôle. En fait, belaran@999: votre contrôle sur votre projet est illusoire, vous ne faites belaran@999: qu'interdire à vos collaborateurs de travailler de manière fluide, en belaran@999: disposant d'un miroir ou d'un fork de votre belaran@999: historique. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Avantages pour les projets commerciaux belaran@999: belaran@999: Beaucoup de projets commerciaux sont réalisés par des belaran@999: équipes éparpillées à travers le globe. Les contributeurs qui sont belaran@999: loin du serveur central devront subir des commandes lentes et même belaran@999: parfois peu fiables. Les solutions propriétaires de gestion de source belaran@999: tentent de palier ce problème avec des réplications de sites distants belaran@999: qui sont à la fois coûteuses à mettre en place et lourdes à belaran@999: administrer. Un système distribué ne souffre pas de ce genre de belaran@999: problèmes. En outre, il est très aisé de mettre en place plusieurs belaran@999: serveurs de références, disons un par site, de manière à ce qu'il n'y belaran@999: ait pas de communication redondante entre les dépôts, sur une belaran@999: connexion longue distance souvent onéreuse. belaran@999: belaran@999: belaran@999: Les systèmes de gestion de source supportent belaran@999: généralement assez mal la monté en charge. Il n'est pas rare pour un belaran@999: gestionnaire de source centralisé pourtant onéreux de s'effondrer belaran@999: sous la charge combinée d'une douzaine d'utilisateurs concurrents belaran@999: seulement. Une fois encore, la réponse à cette problématique est belaran@999: généralement encore la mise en place d'un ensemble complexe de belaran@999: serveurs synchronisés par un mécanisme de réplication. Dans le cas belaran@999: d'un gestionnaire de source distribué, la charge du serveur central belaran@999: — si vous avez un— est plusieurs fois inférieure (car belaran@999: toutes les données sont déjà répliquées ailleurs), un simple serveur, belaran@999: pas très cher, peut gérer les besoins d'une plus grande équipe, et la belaran@999: réplication pour balancer la charge devient le travail d'un simple belaran@999: script. belaran@999: belaran@999: belaran@999: Si vous avez des employés sur le terrain, en train de belaran@999: chercher à résoudre un souci sur le site d'un client, ils belaran@999: bénéficieront aussi d'un gestionnaire de source distribué. Cet outil belaran@999: leur permettra de générer des versions personnalisées, d'essayer belaran@999: différentes solutions, en les isolant aisément les unes des autres, belaran@999: et de rechercher efficacement à travers l'historique des sources, la belaran@999: cause des bugs ou des régressions, tout ceci sans avoir besoin de la belaran@999: moindre connexion au réseau de votre compagnie. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Pourquoi choisir Mercurial? belaran@999: belaran@999: Mercurial a plusieurs caractéristiques qui en font un belaran@999: choix particulièrement pertinent pour la gestion de source : belaran@999: belaran@999: belaran@999: Il est simple à apprendre et à utiliser. belaran@999: Il est léger. belaran@999: Il s'adapte très bien à la charge. belaran@999: Il se personnalise facilement. belaran@999: belaran@999: belaran@999: Si vous êtes déjà familier d'un outil de gestion de belaran@999: source, vous serez capable de l'utiliser en moins de 5 minutes. Sinon, belaran@999: ça ne sera pas beaucoup plus long. Les commandes utilisées par belaran@999: Mercurial, comme ses fonctionnalités, sont généralement uniformes et belaran@999: cohérentes, et vous pouvez ainsi garder en tête simplement quelques belaran@999: règles générales, plutôt qu'un lot complexe d'exceptions. belaran@999: belaran@999: belaran@999: Sur un petit projet, vous pouvez commencer à travailler belaran@999: avec Mercurial en quelques instants. Ajouter des modifications ou des belaran@999: branches, transférer ces modifications (localement ou via le réseau), belaran@999: et les opérations d'historique ou de statut sont aussi très rapides. belaran@999: Mercurial reste hors de votre chemin grâce à sa simplicité belaran@999: d'utilisation et sa rapidité d'exécution. belaran@999: belaran@999: belaran@999: L'utilité de Mercurial ne se limite pas à de petits belaran@999: projets: il est aussi utilisé par des projets ayant des centaines ou belaran@999: même des milliers de contributeurs, avec plusieurs dizaines de milliers belaran@999: de fichiers, et des centaines de méga octets de code source. belaran@999: belaran@999: belaran@999: Si les fonctionnalités au cœur de Mercurial ne sont pas belaran@999: suffisantes pour vous, il est très aisé d'en construire d'autres. belaran@999: Mercurial est adapté à l'utilisation de scripts, et son implémentation belaran@999: interne en Python, propre et claire, rend encore plus facile l'ajout de belaran@999: fonctionnalités sous forme d'extensions. Il en existe déjà un certain belaran@999: nombre de très populaires et très utiles, dont le périmètre va de la belaran@999: recherche de bugs à l'amélioration des performances. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial comparé aux autres outils belaran@999: belaran@999: Avant que vous n'alliez plus loin, comprenez bien que belaran@999: cette section reflète mes propres expériences, et elle est donc (j'ose belaran@999: le dire) peu objective. Néanmoins, j'ai utilisé les outils de gestion belaran@999: de source listés ci dessous, dans la plupart des cas, pendant plusieurs belaran@999: années. belaran@999: belaran@999: belaran@999: belaran@999: Subversion belaran@999: belaran@999: Subversion est un des outils de gestion de source les belaran@999: plus populaire, il fût développé pour remplacer CVS. Il a une belaran@999: architecture client/server centralisée. belaran@999: belaran@999: belaran@999: Subversion et Mercurial ont des noms de commandes très belaran@999: similaires pour les mêmes opérations, ainsi si vous êtes familier belaran@999: avec l'un, c'est facile d'apprendre l'autre. Ces deux outils sont belaran@999: portables sur les systèmes d'exploitation les plus populaires. belaran@999: belaran@999: belaran@999: Avant la version 1.5, Subversion n'offrait aucune forme belaran@999: de support pour les fusions. Lors de l'écriture de ce livre, ses belaran@999: capacités de fusion étaient nouvelles, et réputées pour être belaran@999: complexes et buguées. belaran@999: belaran@999: belaran@999: Mercurial dispose d'un avantage substantiel en terme de belaran@999: performance par rapport à Subversion sur la plupart des opérations belaran@999: que j'ai pu tester. J'ai mesuré une différence de performance allant belaran@999: de deux à six fois plus rapide avec le système de stockage de fichier belaran@999: local de Subversion 1.4.3 (ra_local), qui est la belaran@999: méthode d'accès la plus rapide disponible. Dans un déploiement plus belaran@999: réaliste, impliquant un stockage réseau, Subversion serait encore belaran@999: plus désavantagé. Parce que la plupart des commandes Subversion belaran@999: doivent communiquer avec le serveur et que Subversion n'a pas de belaran@999: mécanisme de réplication, la capacité du serveur et la bande passante belaran@999: sont devenues des goulots d'étranglement pour les projets de taille belaran@999: moyenne ou grande. belaran@999: belaran@999: belaran@999: En outre, Subversion implique une surcharge belaran@999: substantielle dans le stockage local de certaines données, pour belaran@999: éviter des transactions avec le serveur, pour certaines opérations belaran@999: communes, telles que la recherche des fichiers modifiés belaran@999: (status) et l'affichage des modifications par belaran@999: rapport à la révision courante (diff). En belaran@999: conséquence, un répertoire de travail Subversion a souvent la même belaran@999: taille, ou est plus grand, qu'un dépôt Mercurial et son espace de belaran@999: travail, et ceci bien que le dépôt Mercurial contienne l'intégralité belaran@999: de l'historique. belaran@999: belaran@999: belaran@999: Subversion est largement supporté par les outils belaran@999: tierces. Mercurial est actuellement encore en retrait de ce point de belaran@999: vue. L'écart se réduit néanmoins, en effet, certains des outils belaran@999: graphiques sont maintenant supérieurs à leurs équivalents Subversion. belaran@999: Comme Mercurial, Subversion dispose d'un excellent manuel belaran@999: utilisateur. belaran@999: belaran@999: belaran@999: Parce que Subversion ne stocke pas l'historique chez belaran@999: ses clients, il est parfaitement adapté à la gestion de projets qui belaran@999: doivent suivre un ensemble de larges fichiers binaires et opaques. Si belaran@999: vous suivez une cinquantaine de versions d'un fichier incompressible belaran@999: de 10MB, l'occupation disque coté client d'un projet sous Subversion belaran@999: restera à peu près constante. A l'inverse, l'occupation disque du belaran@999: même projet sous n'importe lequel des gestionnaires de source belaran@999: distribués grandira rapidement, proportionnellement aux nombres de belaran@999: versions, car les différences entre chaque révisions seront très belaran@999: grandes. belaran@999: belaran@999: belaran@999: En outre, c'est souvent difficile ou, généralement, belaran@999: impossible de fusionner des différences dans un fichier binaire. La belaran@999: capacité de Subversion de verrouiller des fichiers, pour permettre à belaran@999: l'utilisateur d'être le seul à le mettre à jour belaran@999: (commit) temporairement, est un avantage significatif belaran@999: dans un projet doté de beaucoup de fichiers binaires. belaran@999: belaran@999: belaran@999: Mercurial peut importer l'historique depuis un dépôt belaran@999: Subversion. Il peut aussi exporter l'ensemble des révisions d'un belaran@999: projet vers un dépôt Subversion. Ceci rend très facile de belaran@999: prendre la température et d'utiliser Mercurial et belaran@999: Subversion en parallèle, avant de décider de migrer vers Mercurial. belaran@999: La conversion de l'historique est incrémentale, donc vous pouvez belaran@999: effectuer une conversion initiale, puis de petites additions par la belaran@999: suite pour ajouter les nouvelle modifications. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Git belaran@999: belaran@999: Git est un outil de gestion de source distribué qui fût belaran@999: développé pour gérer le code source de noyau de Linux. Comme belaran@999: Mercurial, sa conception initiale a été inspirée par Monotone. belaran@999: belaran@999: belaran@999: Git dispose d'un ensemble conséquent de commandes, avec belaran@999: plus de 139 commandes individuelles pour la version 1.5.0. Il a aussi belaran@999: la réputation d'être difficile à apprendre. Comparé à Git, le point belaran@999: fort de Mercurial est clairement sa simplicité. belaran@999: belaran@999: belaran@999: En terme de performance, Git est extrêmement rapide. belaran@999: Dans la plupart des cas, il est plus rapide que Mercurial, tout du belaran@999: moins sur Linux, alors que Mercurial peut être plus performant sur belaran@999: d'autres opérations. Néanmoins, sur Windows, les performances et le belaran@999: niveau de support général fourni par Git, au moment de l'écriture de belaran@999: cet ouvrage, est bien derrière celui de Mercurial. belaran@999: belaran@999: belaran@999: Alors que le dépôt Mercurial ne demande aucune belaran@999: maintenance, un dépôt Git exige d'exécuter manuellement et belaran@999: régulièrement la commande repacks sur ses métadonnées. belaran@999: Sans ceci, les performances de git se dégradent et la consommation de belaran@999: l'espace disque augmente rapidement. Un serveur qui contient belaran@999: plusieurs dépôts Git qui ne sont pas régulièrement et fréquemment belaran@999: repacked deviendra un vrai problème lors des belaran@999: backups du disque, et il y eu des cas, où un belaran@999: backup journalier pouvait durer plus de 24 heures. Un belaran@999: dépôt fraichement repacked sera légèrement plus petit belaran@999: qu'un dépôt Mercurial, mais un dépôt non repacked est belaran@999: beaucoup plus grand. belaran@999: belaran@999: belaran@999: Le cœur de Git est écrit en C. La plupart des commandes belaran@999: Git sont implémentées sous forme de scripts Shell ou Perl, et la belaran@999: qualité de ces scripts varie grandement. J'ai plusieurs fois constaté belaran@999: que certains de ces scripts étaient chargés en mémoire aveuglément et belaran@999: que la présence d'erreurs pouvait s'avérer fatal. belaran@999: belaran@999: belaran@999: Mercurial peut importer l'historique d'un dépôt Git. belaran@999: belaran@999: belaran@999: belaran@999: CVS belaran@999: belaran@999: CVS est probablement l'outil de gestion de source le belaran@999: plus utilisé aujourd'hui dans le monde. À cause de son manque de belaran@999: clarté interne, il n'est plus maintenu depuis plusieurs années. belaran@999: belaran@999: belaran@999: Il a une architecture client/serveur centralisée. Il ne belaran@999: regroupe pas les modifications de fichiers dans une opération de belaran@999: commit atomique, ce qui permet à ses utilisateurs de belaran@999: casser le build assez facilement belaran@999: : une personne peut effectuer une opération de commit belaran@999: sans problème puis être bloquée par besoin de fusion, avec comme belaran@999: conséquence néfaste, que les autres utilisateurs ne récupèreront belaran@999: qu'une partie de ses modifications. Ce problème affecte aussi la belaran@999: manière de travailler avec l'historique du projet. Si vous voulez belaran@999: voir toutes les modifications d'une personne du projet, vous devrez belaran@999: injecter manuellement les descriptions et les timestamps des modifications de chacun des belaran@999: fichiers impliqués (si vous savez au moins quels sont ces fichiers). belaran@999: belaran@999: belaran@999: CVS a une notion étrange des tags et des branches que je n'essayerai même belaran@999: pas de décrire ici. Il ne supporte pas bien les opérations de belaran@999: renommage d'un fichier ou d'un répertoire, ce qui facilite la belaran@999: corruption de son dépôt. Il n'a presque pas pour ainsi dire de belaran@999: contrôle de cohérence interne, il est donc pratiquement impossible de belaran@999: dire si un dépôt est corrompu ni à quel point. Je ne recommanderai belaran@999: pas CVS pour un projet existant ou nouveau. belaran@999: belaran@999: belaran@999: Mercurial peut importer l'historique d'un projet CVS. belaran@999: Néanmoins, il y a quelques principes à respecter; ce qui est vrai belaran@999: aussi pour les autres outils d'import de projet CVS. À cause de belaran@999: l'absence de commit atomique et gestion de version de belaran@999: l'arborescence, il n'est pas possible de reconstruire de manière belaran@999: précise l'ensemble de l'historique. Un travail de belaran@999: devinette est donc nécessaire, et les fichiers belaran@999: renommés ne sont pas détectés. Parce qu'une bonne part de belaran@999: l'administration d'un dépôt CVS est effectuée manuellement, et est belaran@999: donc, sujette à erreur, il est courant que les imports CVS belaran@999: rencontrent de nombreux problèmes avec les dépôt corrompus (des belaran@999: timestamps de révision complètement belaran@999: buggés et des fichiers verrouillés depuis des années sont deux des belaran@999: problèmes les moins intéressants dont je me souvienne). belaran@999: belaran@999: belaran@999: Mercurial peut importer l'historique depuis un dépôt CVS. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Outils propriétaires belaran@999: belaran@999: Perforce a une architecture client/serveur centralisée, belaran@999: sans aucun mécanisme de mise en cache de données coté client. belaran@999: Contrairement à la plupart des outils modernes de gestion de source, belaran@999: Perforce exige de ses utilisateurs d'exécuter une commande pour belaran@999: informer le serveur central de tout fichier qu'ils souhaitent belaran@999: modifier. belaran@999: belaran@999: belaran@999: Les performances de Perforce sont plutôt bonnes pour belaran@999: des petites équipes, mais elles s'effondrent rapidement lorsque le belaran@999: nombre d'utilisateurs augmente au delà de la douzaine. Des belaran@999: installations de Perforce assez larges nécessitent le déploiement de belaran@999: proxies pour supporter la montée en charge associée. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Choisir un outil de gestion de source belaran@999: belaran@999: A l'exception de CVS, tous les outils listés ci-dessus belaran@999: ont des forces qui leur sont propres et qui correspondent à certaines belaran@999: formes de projet. Il n'y a pas un seul meilleur outil de gestion de belaran@999: source qui correspondrait le mieux à toutes les situations. belaran@999: belaran@999: belaran@999: En guise exemple, Subversion est un très bon choix belaran@999: lorsqu'on travaille avec beaucoup de fichiers binaires, qui évoluent belaran@999: régulièrement, grâce à sa nature centralisée et sa capacité à belaran@999: verrouiller des fichiers. belaran@999: belaran@999: belaran@999: Personnellement, je préfère Mercurial pour sa belaran@999: simplicité, ses performances et sa bonne capacité de fusion, et il belaran@999: m'a très bien rendu service de plusieurs années maintenant. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Migrer depuis un outil à Mercurial belaran@999: belaran@999: Mercurial est livré avec une extension nommée convert, qui peut, de manière incrémentale belaran@999: importer des révisions depuis différents autres outils de gestion de belaran@999: source. Par incrémental, j'entends que vous pouvez belaran@999: convertir l'historique entier du projet en une seule fois, puis belaran@999: relancer l'outil d'import plus tard pour obtenir les modifications belaran@999: effectuées depuis votre import initial. belaran@999: belaran@999: belaran@999: Les outils de gestion de source supportés par convert sont : belaran@999: belaran@999: belaran@999: Subversion belaran@999: CVS belaran@999: Git belaran@999: Darcs belaran@999: belaran@999: belaran@999: En outre, convert peut belaran@999: exporter les modifications depuis Mercurial vers Subversion. Ceci rend belaran@999: possible d'essayer Subversion en parallèle avant de choisir une belaran@999: solution définitive, sans aucun risque de perte de données. belaran@999: belaran@999: belaran@999: La commande convert est très simple à utiliser. belaran@999: Simplement, indiquez le chemin ou l'URL du dépôt de source, en lui belaran@999: indiquant éventuellement le nom du chemin de destination, et la belaran@999: conversion se met en route. Après cet import initial, il suffit de belaran@999: relancer la commande encore une fois pour importer les modifications belaran@999: effectuées depuis. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Une courte histoire de la gestion de source belaran@999: belaran@999: Le plus célèbre des anciens outils de gestion de source belaran@999: est SCCS (Source Code Control System)}, belaran@999: que Marc Rochkind conçu dans les laboratoires de recherche de Bell belaran@999: (Bell Labs), dans le début des années belaran@999: 70. SCCS ne fonctionnait que sur des belaran@999: fichiers individuels, et obligeait chaque personne travaillant sur le belaran@999: projet d'avoir un accès à un répertoire de travail commun, sur le même belaran@999: système. Seulement une seule personne pouvait modifier un fichier au belaran@999: même moment, ce fonctionnement était assuré par l'utilisation de verrou belaran@999: (lock). Il était courant que des personnes verrouillent belaran@999: des fichiers, et plus tard, oublient de le déverrouiller ; empêchant belaran@999: n'importe qui d'autre de travailler sur ces fichiers sans l'aide de belaran@999: l'administrateur... belaran@999: belaran@999: belaran@999: Walter Tichy a développé une alternative libre à belaran@999: SCCS au début des années 80, qu'il belaran@999: nomma RCS (Revision Control System). belaran@999: Comme SCCS, RCS demandait aux développeurs de travailler belaran@999: sur le même répertoire partagé, et de verrouiller les fichiers pour se belaran@999: prémunir de tout conflit issu de modifications concurrentes. belaran@999: belaran@999: belaran@999: Un peu plus tard dans les années 1980, Dick Grune utilisa belaran@999: RCS comme une brique de base pour un belaran@999: ensemble de scripts shell qu'il belaran@999: intitula cmt, avant de la renommer en CVS belaran@999: (Concurrent Versions System). La grande innovation de CVS belaran@999: était que les développeurs pouvaient travailler simultanément et belaran@999: indépendamment dans leur propre espace de travail. Ces espaces de belaran@999: travail privés assuraient que les développeurs ne se marchent pas belaran@999: mutuellement sur les pieds, comme c'était souvent le cas avec RCS et belaran@999: SCCS. Tous les développeurs disposaient donc de leur copie de tous les belaran@999: fichiers du projet, et ils pouvaient donc librement les modifier. Ils belaran@999: devaient néanmoins effectuer la fusion (merge) de leurs fichiers, avant belaran@999: d'effectuer le commit de leurs modifications sur le dépôt belaran@999: central. belaran@999: belaran@999: belaran@999: Brian Berliner reprit les scripts de Grune's et les réécrit en C, belaran@999: qu'il publia en 1989. Depuis, ce code a été modifié jusqu'à devenir la belaran@999: version moderne de CVS. CVS a acquis ainsi la capacité de fonctionner belaran@999: en réseau, transformant son architecture en client/serveur. belaran@999: L'architecture de CVS est centralisée, seul le serveur a une copie de belaran@999: l'historique du projet. L'espace de travail client ne contient qu'une belaran@999: copie de la dernière version du projet, et quelques métadonnées pour belaran@999: indiquer où le serveur se trouve. CVS a été un grand succès, belaran@999: aujourd'hui il est probablement l'outil de gestion de contrôle le plus belaran@999: utilisé au monde. belaran@999: belaran@999: belaran@999: Au début des années 1990, Sun Microsystems développa un premier belaran@999: outil de gestion de source distribué, nommé TeamWare. Un espace de belaran@999: travail TeamWare contient une copie complète de l'historique du projet. belaran@999: TeamWare n'a pas de notion de dépôt central. (CVS utilisait RCS pour le belaran@999: stockage de l'historique, TeamWare utilisait SCCS). belaran@999: belaran@999: belaran@999: Alors que les années 1990 avançaient, les utilisateurs ont pris belaran@999: conscience d'un certain nombre de problèmes avec CVS. Il enregistrait belaran@999: simultanément des modifications sur différents fichiers belaran@999: individuellement, au lieu de les regrouper dans une seule opération belaran@999: cohérente et atomique. Il ne gère pas bien sa hiérarchie de fichier, il belaran@999: est donc assez aisé de créer le chaos en renommant les fichiers et les belaran@999: répertoires. Pire encore, son code source est difficile à lire et à belaran@999: maintenir, ce qui agrandit largement le niveau de belaran@999: souffrance associé à la réparation de ces problèmes belaran@999: d'architecture de manière prohibitive. belaran@999: belaran@999: belaran@999: En 2001, Jim Blandy et Karl Fogel, deux développeurs qui avaient belaran@999: travaillé sur CVS, initièrent un projet pour le remplacer par un outil belaran@999: qui aurait une meilleure architecture et un code plus propre. Le belaran@999: résultat, Subversion, ne quitte pas le modèle centralisé et belaran@999: client/server de CVS, mais ajoute les opérations de belaran@999: commit atomique sur de multiples fichiers, une meilleure belaran@999: gestion des espaces de noms, et d'autres fonctionnalités qui en font un belaran@999: meilleur outil que CVS. Depuis sa première publication, il est belaran@999: rapidement devenu très populaire. belaran@999: belaran@999: belaran@999: Plus ou moins simultanément, Graydon Hoare a commencé sur belaran@999: l'ambitieux système de gestion distribué Monotone. Bien que Monotone belaran@999: corrige plusieurs défauts de CVS tout en offrant une architecture belaran@999: peer-to-peer, il va aussi plus loin que la plupart des belaran@999: outils de révision de manière assez innovante. Il utilise des belaran@999: hashs cryptographiques comme identifiants, et il a une belaran@999: notion complète de confiance du code issu des belaran@999: différentes sources. belaran@999: belaran@999: belaran@999: Mercurial est né en 2005. Bien que très influencé par Monotone, belaran@999: Mercurial se concentre sur la facilité d'utilisation, les performances belaran@999: et la capacité à monter en charge pour de très gros projets. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Une rapide présentation de Mercurial : les bases belaran@999: belaran@999: belaran@999: Installer Mercurial sur votre système belaran@999: belaran@999: Des paquetages binaires de Mercurial sont disponibles pour la belaran@999: plupart des systèmes d'exploitation, ce qui rend facile l'utilisation belaran@999: immédiate de Mercurial sur votre ordinateur. belaran@999: belaran@999: belaran@999: Windows belaran@999: belaran@999: La meilleur version de Mercurial pour Windows est belaran@999: TortoiseHg, qui peut être téléchargée ici : http://bitbucket.org/tortoisehg/stable/wiki/Home. belaran@999: Ce logiciel n'a aucune dépendance exterieure; il fonctionne et belaran@999: c'est tout. Il fournit aussi bien les outils en ligne de belaran@999: commmande qu'une interface graphique. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mac OS X belaran@999: belaran@999: Lee Cantey publie un installeur de Mercurial pour Mac OS belaran@999: X sur http://mercurial.berkwood.com. belaran@999: belaran@999: belaran@999: belaran@999: Linux belaran@999: belaran@999: Parce que chaque distribution de Linux a ses propres belaran@999: outils de gestion de paquets, politiques et rythmes de belaran@999: développements, il est difficile de donner un ensemble belaran@999: d'instructions unique pour installer les binaires de Mercurial. La belaran@999: version de Mercurial avec laquelle vous vous retrouverez dépendra belaran@999: grandement de l'activité de la personne en charge du paquetage pour belaran@999: la distribution. belaran@999: belaran@999: Pour rester simple, je me concentrerai sur belaran@999: l'installation de Mercurial en ligne de commande, sous les belaran@999: distributions les plus courantes. La plupart des distributions belaran@999: fournissent des gestionnaires graphiques de paquetage qui vous belaran@999: permettront d'installer Mercurial en quelques clicks. Le paquetage belaran@999: devrait se nommer mercurial. belaran@999: belaran@999: belaran@999: Ubuntu et Debian: belaran@999: apt-get install mercurial belaran@999: Fedora: belaran@999: yum install mercurial belaran@999: Gentoo: belaran@999: emerge mercurial belaran@999: OpenSUSE: belaran@999: zypper install belaran@999: mercurial belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Solaris belaran@999: belaran@999: SunFreeWare, à http://www.sunfreeware.com, belaran@999: fournit des paquets précompilés pour Mercurial. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Commencer à utiliser Mercurial belaran@999: belaran@999: Pour commencer, nous utiliserons la commande hg version pour vérifier si Mercurial est belaran@999: installé proprement. Les informations affichées sur la version ne sont belaran@999: pas réellement importantes en soit, c'est surtout de savoir si elles belaran@999: s'affichent qui nous intéresse. belaran@999: belaran@999: belaran@999: $ hg version belaran@999: Mercurial Distributed SCM (version 1.2.1) belaran@999: belaran@999: Copyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others belaran@999: This is free software; see the source for copying conditions. There is NO belaran@999: warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: L'aide intégrée belaran@999: belaran@999: Mercurial fournit un système d'aide intégré, ce qui est belaran@999: inestimable quand vous vous retrouvez coincé à essayer de vous belaran@999: rappeler comment lancer une commande. Si vous êtes bloqué, exécutez belaran@999: simplement hg help; elle affichera belaran@999: une brève liste des commandes, avec une description pour chacune. Si belaran@999: vous demandez de l'aide sur une commande spécifique (voir belaran@999: ci-dessous), elle affichera des informations plus détaillées. belaran@999: belaran@999: belaran@999: $ hg help init belaran@999: hg init [-e CMD] [--remotecmd CMD] [DEST] belaran@999: belaran@999: create a new repository in the given directory belaran@999: belaran@999: Initialize a new repository in the given directory. If the given belaran@999: directory does not exist, it is created. belaran@999: belaran@999: If no directory is given, the current directory is used. belaran@999: belaran@999: It is possible to specify an ssh:// URL as the destination. belaran@999: See 'hg help urls' for more information. belaran@999: belaran@999: options: belaran@999: belaran@999: -e --ssh specify ssh command to use belaran@999: --remotecmd specify hg command to run on the remote side belaran@999: belaran@999: use "hg -v help init" to show global options belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Pour un niveau d'informations encore plus détaillé belaran@999: (ce dont vous aurez rarement besoin), exécuter hg belaran@999: help . L'option belaran@999: est l'abréviation de belaran@999: , et indique à Mercurial belaran@999: d'ficher plus d'informations que d'habitude. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Travailler avec un dépôt belaran@999: belaran@999: Avec Mercurial, tout se déroule au sein du belaran@999: dépôt. Le dépôt d'un projet contient tous belaran@999: les fichiers qui appartiennent au projet. belaran@999: belaran@999: Il n'y a rien de particulièrement magique au sujet de belaran@999: ce dépôt, c'est simplement une arborescence sur votre système de fichiers belaran@999: que Mercurial traite de manière spéciale. Vous pouvez renommer ou effacer belaran@999: ce répertoire à n'impporte quel moment, en utilisant la ligne de commande belaran@999: ou votre explorateur de fichiers. belaran@999: belaran@999: belaran@999: Faire une copie locale de votre dépôt belaran@999: belaran@999: Copier un dépôt est juste un belaran@999: peu spécial. Bien que vous puissiez utiliser une commande habituelle de belaran@999: copie pour copier votre dépôt, il vaut mieux utiliser une commande fournie par belaran@999: Mercurial. Cette commande est appelée hg clone, belaran@999: car elle crée une copie identique à un dépôt existant. belaran@999: belaran@999: belaran@999: $ hg clone http://hg.serpentine.com/tutorial/hello belaran@999: destination directory: hello belaran@999: requesting all changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 5 changesets with 5 changes to 2 files belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Un avantage de la commande hg belaran@999: clone est que, comme nous l'avons vu ci dessus, elle nous belaran@999: permet de faire de cloner les dépôts à travers le réseau. Un autre belaran@999: est qu'elle se rappelle d'où a été cloné un dépôt, ce qui est utile belaran@999: quand on veut mettre à jour le clone. belaran@999: belaran@999: Si votre opération de clonage réussit, vous devriez maintenant belaran@999: avoir un répertoire local appelé hello. belaran@999: Ce répertoire contiendra quelques fichiers. belaran@999: belaran@999: belaran@999: $ ls -l belaran@999: total 4 belaran@999: drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:05 hello belaran@999: $ ls hello belaran@999: Makefile hello.c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Ces fichiers ont le même contenu et historique dans votre dépôt belaran@999: qu'ils ont dans le dépôt que vous avez cloné. belaran@999: belaran@999: Chaque dépôt Mercurial est complet, autonome et belaran@999: indépendant. Il contient sa propre copie privée des fichiers du belaran@999: projet et de leur historique. Le clone d'un dépôt se souvient de la belaran@999: localisation du dépôt à partir duquel il a été clôné, mais il ne belaran@999: communique pas avec ce dernier, ou un autre, à moins que vous ne lui belaran@999: demandiez. belaran@999: belaran@999: Ce que tout ceci signifie pour le moment est que nous belaran@999: sommes libres d'expérimenter avec ce dépôt, confiants dans le fait belaran@999: qu'il s'agit d'un bac à sable qui n'affectera personne belaran@999: d'autre. belaran@999: belaran@999: belaran@999: belaran@999: Quel est le contenu d'un dépôt ? belaran@999: belaran@999: Prêtons plus attention un instant au contenu d'un dépôt. belaran@999: Nous voyons qu'il contient un répertoire nommé .hg belaran@999: . C'est ici que Mercurial conserve toutes ses belaran@999: métadonnées. belaran@999: belaran@999: belaran@999: $ cd hello belaran@999: $ ls -a belaran@999: . .. .hg Makefile hello.c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Le contenu du répertoire .hg belaran@999: et ses sous répertoires sont les seuls propres à Mercurial. belaran@999: Tous les autres fichiers et répertoires dans le dépôt sont à vous, et belaran@999: vous pouvez en faire ce que vous voulez. belaran@999: belaran@999: Pour introduire un peu de terminologie, le répertoire belaran@999: .hg est un vrai belaran@999: dépôt, et tous les fichiers et les répertoires qui coexistent avec lui, belaran@999: sont désignés sous le nom espace de travail. Une belaran@999: manière facile de se rappeler cette distinction est de retenir que le belaran@999: dépôt contient l'historique belaran@999: de votre projet, alors que l'espace de travail belaran@999: contient un "snapshot" de votre projet à un certain belaran@999: point de son historique. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Une promenade dans l'historique belaran@999: belaran@999: Une des premières choses que vous aurez envie belaran@999: de faire avec un nouveau dépôt, sera de comprendre son historique. belaran@999: La commande hg log vous donne une belaran@999: vue de l'historique. belaran@999: belaran@999: belaran@999: $ hg log belaran@999: changeset: 4:2278160e78d4 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:16:53 2008 +0200 belaran@999: summary: Trim comments. belaran@999: belaran@999: changeset: 3:0272e0d5a517 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:08:02 2008 +0200 belaran@999: summary: Get make to generate the final binary from a .o file. belaran@999: belaran@999: changeset: 2:fef857204a0c belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:05:04 2008 +0200 belaran@999: summary: Introduce a typo into hello.c. belaran@999: belaran@999: changeset: 1:82e55d328c8c belaran@999: user: mpm@selenic.com belaran@999: date: Fri Aug 26 01:21:28 2005 -0700 belaran@999: summary: Create a makefile belaran@999: belaran@999: changeset: 0:0a04b987be5a belaran@999: user: mpm@selenic.com belaran@999: date: Fri Aug 26 01:20:50 2005 -0700 belaran@999: summary: Create a standard "hello, world" program belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Par défaut, cette commande affiche à l'écran un bref paragraphe pour chaque belaran@999: révision enregistrée pour ce projet. Dans la terminologie de Mercurial, nous belaran@999: appelons chacun de ces évènements enregistrés un changeset, parce belaran@999: qu'il contient un ensemble de modifications sur plusieurs fichiers. belaran@999: belaran@999: La commande hg log affiche belaran@999: ainsi ces informations : belaran@999: belaran@999: belaran@999: changeset : Ce champ contient belaran@999: un nombre, séparé par deux points (:), d'une chaine hexadécimale. Il belaran@999: s'agit en fait d'identifiants d'un changeset. Il y a belaran@999: deux identifiants car le numéro de la révision est plus court et plus à belaran@999: facile à saisir qu'une séquence hexadécimale. belaran@999: belaran@999: user : L'identité de la personne belaran@999: qui a créée ce %%% laisser le terme anglais car il sera affiché belaran@999: changeset. C'est un champ libre de forme, mais la plupart du belaran@999: temps il contient le nom et l'email de la personne. belaran@999: belaran@999: date : La date et l'heure à belaran@999: laquelle le \textit{changeset} a été créé, ainsi que le fuseau horaire dans belaran@999: lequelle il a été créé. (La date et l'heure sont locales à ce belaran@999: \textit{fuseau}, elles indiquent donc quelle date et heure il était belaran@999: pour la personne qui a créé ce changeset. belaran@999: belaran@999: résumé: La première ligne du belaran@999: message que le créateur a associé à son changeset pour le décrire. belaran@999: belaran@999: Certains changesets, comme le premier de la belaran@999: liste ci-dessus ont un champ tag. Le tag est une autre belaran@999: façon d'identifier un changeset en lui donnant un nom simple à retenir. belaran@999: (Le tag nommé tip est spécial : il fait toujours belaran@999: référence aux derniers changements dans le dépôt.) belaran@999: belaran@999: belaran@999: Par défaut, la commande hg log belaran@999: n'affiche qu'un résumé, il manque beaucoup de détails. belaran@999: belaran@999: La figure fournit une belaran@999: représentation graphique de l'historique du dépôt hello belaran@999: , pour rendre plus facile de voir dans quelle direction belaran@999: l'historique se déroule. Nous reviendrons régulièrement belaran@999: sur cette représentation dans ce chapitre et ceux qui suivent. belaran@999: belaran@999: belaran@999:
belaran@999: Graphical history of the <filename class="directory" moreinfo="none">hello</filename> repository belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: Changesets, révisions, et collaboration belaran@999: belaran@999: Comme l'anglais est réputé pour être un langage maladroit, belaran@999: et que l'informatique est la source de bien des erreurs de terminologie belaran@999: (pourquoi utiliser un seul terme quand quatre feront l'affaire ?), la belaran@999: gestion de version a une variété de mots et de phrases qui veulent dire belaran@999: la même chose. Si vous discutez d'historique de Mercurial avec d'autres belaran@999: personnes, vous constaterez que souvent, le mot changeset belaran@999: est contracté simplement en change ou (à l'écrit) belaran@999: cset, et même parfois un changeset belaran@999: révision, abrégé en rev. belaran@999: belaran@999: Bien que le mot que vous utilisez pour belaran@999: désigner le concept de changeset importe peu, l'identifiant belaran@999: que vous utilisez pour désigner un changeset spécifique belaran@999: a une grande importance. Rappelez vous que le champ changeset affiché par la belaran@999: commande hg log identifie un changeset à belaran@999: la fois avec un numéro de révision et une séquence hexadécimale. belaran@999: belaran@999: belaran@999: Le numéro de révision est seulement belaran@999: valable dans ce dépôt, belaran@999: La séquence hexadécimale est un belaran@999: identifiant permanent, et invariant qui belaran@999: pourra toujours être associé au changeset exact de chaque belaran@999: copie de votre dépôt. belaran@999: belaran@999: La distinction est importante. Si vous envoyez un email belaran@999: à quelqu'un en parlant de la révision 33, il est très belaran@999: probable que sa révision 33 ne sera pas la même belaran@999: que la votre. La raison de ceci est que le numéro de révision dépend belaran@999: de l'ordre dans lequel les modifications sont arrivées dans le dépôt, belaran@999: et il n'y a aucune garantie que les mêmes changements soient arrivés belaran@999: dans le même ordre dans différents dépôts. Trois modifications belaran@999: a,b,c peuvent aisément apparaitre dans un dépôt belaran@999: comme 0,1,2, et dans un autre comme 0,2,1 belaran@999: . belaran@999: belaran@999: Mercurial utilise les numéros de révision uniquement comme des raccourcis belaran@999: pratiques. Si vous devez discuter d'un \textit{changeset} avec quelqu'un, belaran@999: ou identifer un \textit{changeset} pour une quelconque raison (par exemple, belaran@999: un rapport de \textit{bug}), utilisez la séquence hexadécimale. belaran@999: belaran@999: belaran@999: belaran@999: Afficher une révision spécifique belaran@999: belaran@999: Pour réduire la sortie de hg log belaran@999: à une seule révision, utilisez l'option (ou ). Vous pouvez utiliser belaran@999: le numéro de révision ou la séquence hexadécimale comme identifiant, et belaran@999: demander autant de révisions que vous le souhaitez. belaran@999: belaran@999: belaran@999: $ hg log -r 3 belaran@999: changeset: 3:0272e0d5a517 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:08:02 2008 +0200 belaran@999: summary: Get make to generate the final binary from a .o file. belaran@999: belaran@999: $ hg log -r 0272e0d5a517 belaran@999: changeset: 3:0272e0d5a517 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:08:02 2008 +0200 belaran@999: summary: Get make to generate the final binary from a .o file. belaran@999: belaran@999: $ hg log -r 1 -r 4 belaran@999: changeset: 1:82e55d328c8c belaran@999: user: mpm@selenic.com belaran@999: date: Fri Aug 26 01:21:28 2005 -0700 belaran@999: summary: Create a makefile belaran@999: belaran@999: changeset: 4:2278160e78d4 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:16:53 2008 +0200 belaran@999: summary: Trim comments. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si vous voulez voir l'historique de plusieurs révisions belaran@999: sans avoir à les énumérer, vous pouvez utiliser la intervalle belaran@999: de numérotation qui vous permet d'exprimer l'idée je belaran@999: veux toutes les révisions entre $a$ et $b$, inclus belaran@999: belaran@999: belaran@999: $ hg log -r 2:4 belaran@999: changeset: 2:fef857204a0c belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:05:04 2008 +0200 belaran@999: summary: Introduce a typo into hello.c. belaran@999: belaran@999: changeset: 3:0272e0d5a517 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:08:02 2008 +0200 belaran@999: summary: Get make to generate the final binary from a .o file. belaran@999: belaran@999: changeset: 4:2278160e78d4 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:16:53 2008 +0200 belaran@999: summary: Trim comments. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial respecte aussi l'ordre dans lequel vous spécifiez belaran@999: les révisions, ainsi hg log -r 2:4 belaran@999: affichera 2,3,4 alors que hg belaran@999: log -r 4:2 affichera 4,3,2. belaran@999: belaran@999: belaran@999: belaran@999: Informations détaillées belaran@999: belaran@999: Le résumé affiché par hg log belaran@999: est suffisant si vous savez déjà ce que vous cherchez. En belaran@999: revanche, vous aurez probablement besoin de voir une description belaran@999: complète du changement, ou une liste des fichiers modifiés si vous belaran@999: cherchez à déterminer qu'un changeset est bien celui que vous belaran@999: recherchez. L'option \hgopt{-v} de la commande hg belaran@999: log (ou ) vous belaran@999: donne ces informations supplémentaires. belaran@999: belaran@999: belaran@999: $ hg log -v -r 3 belaran@999: changeset: 3:0272e0d5a517 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:08:02 2008 +0200 belaran@999: files: Makefile belaran@999: description: belaran@999: Get make to generate the final binary from a .o file. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si vous voulez voir à la fois la description belaran@999: et le contenu d'une modification, ajouter l'option (ou ). Ceci affiche le contenu d'une modification belaran@999: comme un diff unifié belaran@999: belaran@999: (si vous n'avez jamais vu de diff unifié avant, consultez la belaran@999: section pour un rapide belaran@999: survol). belaran@999: belaran@999: belaran@999: $ hg log -v -p -r 2 belaran@999: changeset: 2:fef857204a0c belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:05:04 2008 +0200 belaran@999: files: hello.c belaran@999: description: belaran@999: Introduce a typo into hello.c. belaran@999: belaran@999: belaran@999: diff -r 82e55d328c8c -r fef857204a0c hello.c belaran@999: --- a/hello.c Fri Aug 26 01:21:28 2005 -0700 belaran@999: +++ b/hello.c Sat Aug 16 22:05:04 2008 +0200 belaran@999: @@ -11,6 +11,6 @@ belaran@999: belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: - printf("hello, world!\n"); belaran@999: + printf("hello, world!\"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: L'option est belaran@999: incroyablement utile, il est donc important dans s'en rappeller. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: Tout sur les options de commandes belaran@999: belaran@999: Avant d'aller plus loin sur le fonctionnement belaran@999: des commandes de Mercurial, étudions un moment comment elles belaran@999: fonctionnent de manière générale. Vous trouverez ça probablement belaran@999: utile pour la suite de notre parcours. belaran@999: belaran@999: Mercurial utilise une approche directe et cohérente belaran@999: pour interpréter les options que vous passez aux commandes. Il suit une belaran@999: convention commune à la plupart des systèmes Unix et Linux modernes. belaran@999: belaran@999: belaran@999: Chaque option a un nom complet. Par exemple, belaran@999: comme nous l'avons déjà vu, la commande hg belaran@999: log accepte l'option . belaran@999: belaran@999: La plupart des options disposent de belaran@999: noms abrégés. Aussi, au lieu d'utiliser , vous pouvez utiliser . belaran@999: (Les options qui n'ont pas de noms abrégés sont généralement belaran@999: rarement utilisées). belaran@999: belaran@999: Les noms complets commencent par deux belaran@999: tirets (i.e. ), belaran@999: alors que les options courtes commencent avec un seul (i.e. belaran@999: ). belaran@999: belaran@999: Les noms des options sont cohérents belaran@999: entre les commandes. Par exemple, chaque commande qui accepte belaran@999: un changeset ID ou un numéro de révision accepte aussi et comme arguments. belaran@999: belaran@999: belaran@999: belaran@999: Dans les exemples de ce livre, j'utilise les noms abrégés belaran@999: plutôt que les noms complets. Ceci est une préférence personnelle, pas belaran@999: une recommandation. belaran@999: belaran@999: La plupart des commandes qui affichent une quelconque sortie belaran@999: à l'écran, afficheront davantage avec l'option (ou ), et belaran@999: moins avec l'option (ou belaran@999: ). belaran@999: belaran@999: belaran@999: Option naming consistency belaran@999: belaran@999: Presque toujours, les commandes de Mercurial utilisent belaran@999: des noms d'options cohérentes pour référer à des concepts identiques. belaran@999: Par exemple, si une commande concerne les changesets, vous les belaran@999: identifierez toujours avec l'option . belaran@999: Cette utilisation cohérente des noms d'options permet de mémoriser plus belaran@999: facilement quelles options accepte une commande. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Faire et vérifier des modifications belaran@999: belaran@999: Maintenant que nous avons une bonne idée des belaran@999: commandes pour consulter l'historique de Mercurial, regardons belaran@999: comment faire des modifications et les examiner. belaran@999: belaran@999: La première chose que nous allons faire c'est isoler notre belaran@999: expérience dans un dépôt à part. Nous allons utiliser la commande hg clone, mais nous n'avons pas besoin de faire belaran@999: une copie de dépôt distant. Comme nous avons déjà une copie locale, nous belaran@999: pouvons juste faire un clone de celle-ci à la place. C'est beaucoup plus belaran@999: rapide que de faire une copie à travers le réseau, et un dépôt cloné belaran@999: localement prend également moins d'espace disque belaran@999: L'économie d'espace disque apparait clairement quand les belaran@999: dépôts source et destination sont sur le même système de fichier, où, dans belaran@999: ce cas, Mercurial utilisera des liens physiques pour effectuer des partages belaran@999: copie-lors-des-écritures de ses métadonnées internes. Si cette explication belaran@999: ne signifie rien pour vous, ne vous inquietez pas : tout ceci se passe de belaran@999: manière transparente et automatiquement. Vous n'avez pas du tout besoin de belaran@999: comprendre ceci.. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone hello my-hello belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd my-hello belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: On notera au passage qu'il est souvent considéré comme belaran@999: une bonne pratique de conserver une copie immaculée belaran@999: du dépôt distant, à partir de laquelle vous pourrez faire des belaran@999: copies locales temporaires pour créer des bacs à sable belaran@999: pour chaque tâche sur laquelle vous souhaitez travailler. Ceci belaran@999: vous permet de travailler sur plusieurs choses en parallèle, belaran@999: chacune isolée les unes des autres en attendant que ces tâches belaran@999: soient finies et que vous soyez prêt à les réintégrer. Parce belaran@999: que les copies locales sont peu coûteuses, il est très rapide belaran@999: de créer ou détruire des dépôts dès que vous n'en avez plus belaran@999: besoin. belaran@999: belaran@999: Dans notre dépôt my-hello, nous avons un fichier belaran@999: hello.c qui contient le classique hello, belaran@999: world. belaran@999: belaran@999: belaran@999: $ cat hello.c belaran@999: /* belaran@999: * Placed in the public domain by Bryan O'Sullivan. This program is belaran@999: * not covered by patents in the United States or other countries. belaran@999: */ belaran@999: belaran@999: #include <stdio.h> belaran@999: belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: printf("hello, world!\"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Editons ce fichier pour qu'il affiche une autre ligne belaran@999: sur la sortie standard. belaran@999: belaran@999: belaran@999: # ... edit edit edit ... belaran@999: $ cat hello.c belaran@999: /* belaran@999: * Placed in the public domain by Bryan O'Sullivan. This program is belaran@999: * not covered by patents in the United States or other countries. belaran@999: */ belaran@999: belaran@999: #include <stdio.h> belaran@999: belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: printf("hello, world!\"); belaran@999: printf("hello again!\n"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La commande Mercurial hg belaran@999: status nous dira ce que Mercurial sait des fichiers du belaran@999: dépôts. belaran@999: belaran@999: belaran@999: $ ls belaran@999: Makefile hello.c belaran@999: $ hg status belaran@999: M hello.c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La commande hg status belaran@999: n'affichera pas le contenu des fichiers, mais une ligne commençant par belaran@999: M pour hello.c. belaran@999: A moins que vous lui demandiez, la commande hg belaran@999: status n'affichera aucune information sur les fichiers que belaran@999: vous n'avez pas modifiés. belaran@999: belaran@999: Le M indique que belaran@999: Mercurial a remarqué que nous avons modifié le fichier belaran@999: hello.c. Nous n'avons pas besoin belaran@999: d'informer Mercurial que nous allons modifier un belaran@999: fichier avant de commencer à le faire, ou que nous avons modifié un belaran@999: fichier après avoir commencé à le faire, il est capable de découvrir ça belaran@999: tout seul. belaran@999: belaran@999: C'est déjà pratique de savoir que nous avons modifié le belaran@999: fichier hello.c, mais nous préférerions savoir belaran@999: exactement ce que nous avons changé. Pour ceci, nous belaran@999: utilisons la commande hg diff. belaran@999: belaran@999: belaran@999: $ hg diff belaran@999: diff -r 2278160e78d4 hello.c belaran@999: --- a/hello.c Sat Aug 16 22:16:53 2008 +0200 belaran@999: +++ b/hello.c Sun Aug 16 14:05:26 2009 +0000 belaran@999: @@ -8,5 +8,6 @@ belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: printf("hello, world!\"); belaran@999: + printf("hello again!\n"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Comprendre les patches belaran@999: belaran@999: Penser à jeter un oeil à si vous n'arrivez pas à lire la sortie belaran@999: ci-dessus. belaran@999: belaran@999: belaran@999: belaran@999: Enregister vos modifications dans une nouvelle révision belaran@999: belaran@999: Nous pouvons modifier des fichiers, compiler et tester belaran@999: nos modifications, et utiliser les commandes hg belaran@999: status et hg diff pour belaran@999: voir les modifications effectuées, jusqu'à ce que nous soyons assez belaran@999: satisfaits pour décider d'enregistrer notre travail dans un belaran@999: \textit{changeset}. belaran@999: belaran@999: La commande hg commit belaran@999: vous laisse créer une nouvelle révision, nous désignerons généralement belaran@999: cette opération par faire un commit ou belaran@999: committer. belaran@999: belaran@999: belaran@999: Définir le nom d'utilisateur belaran@999: belaran@999: Quand vous exécutez la commande hg commit pour la première fois, il n'est belaran@999: pas garanti qu'elle réussisse du premier coup. En effet, Mercurial belaran@999: enregistre votre nom et votre adresse avec chaque modification que belaran@999: vous effectuez, de manière à ce que vous soyez capable (ou d'autres belaran@999: le soient) de savoir qui a fait quelle modification. Mercurial essaye belaran@999: automatiquement de découvrir un nom d'utilisateur qui ait un minimum belaran@999: de sens pour effectuer l'opération de commit avec. Il va essayer belaran@999: chacune des méthodes suivantes, dans l'ordre : belaran@999: belaran@999: belaran@999: Si vous spécifiez l'option avec la commande hg commit, suivi d'un nom belaran@999: d'utilisateur, ceci aura toujours la priorité sur les autres belaran@999: méthodes ci dessous. belaran@999: Si vous avez défini une variable belaran@999: d'environnement HGUSER, c'est cette valeur qui est belaran@999: alors utilisée. belaran@999: Si vous créez un fichier nommé .hgrc dans votre répertoire belaran@999: \textit{home}, avec une entrée username, c'est la valeur associée belaran@999: qui sera utilisée. Pour voir à quoi ressemble le contenu de ce belaran@999: fichier regardez la section belaran@999: ci-dessous. belaran@999: Si vous avez défini une variable belaran@999: d'environnement EMAIL celle ci sera utilisée belaran@999: ensuite. belaran@999: Enfin, Mercurial interrogera votre système belaran@999: pour trouver votre nom d'utilisateur local ainsi que le nom de la belaran@999: machine hôte, et il fabriquera un nom d'utilisateur à partir de belaran@999: ces données. Comme il arrive souvent que ce genre de noms soit belaran@999: totalement inutile, il vous préviendra en affichant un message belaran@999: d'avertissement. belaran@999: belaran@999: belaran@999: Si tous ces mécanismes échouent, Mercurial n'exécutera belaran@999: pas la commande, affichant un message d'erreur. Dans ce cas, il ne belaran@999: vous laissera pas effectuer de commit tant que vous n'aurez pas belaran@999: défini un nom d'utilisateur. belaran@999: belaran@999: Vous devriez penser à utiliser la variable belaran@999: d'environement HGUSER et l'option comme moyen pour belaran@999: changer le nom d'utilisateur par défaut. Pour belaran@999: une utilisation normale, la manière la plus simple et robuste belaran@999: d'opérer est de créer un fichier .hgrc, voir ci-dessous pour les détails belaran@999: à ce sujet. belaran@999: belaran@999: belaran@999: Créer un fichier de configuration pour Mercurial belaran@999: belaran@999: Pour définir un nom d'utilisateur, utilisez votre belaran@999: éditeur de texte favori pour créer un fichier .hgrc dans votre répertoire home. belaran@999: Mercurial va utiliser ce fichier pour retrouver votre belaran@999: configuration personnelle. Le contenu initial devrait belaran@999: ressembler à ceci : belaran@999: belaran@999: belaran@999: <quote>Home directory</quote> sous Windows belaran@999: belaran@999: Quand on parle de répertoire home, sur une version belaran@999: anglaise d'une installation de Windows, il s'agira habituellement belaran@999: d'un répertoire nommée comme votre nom dans C:\Documents belaran@999: and Settings. Vous pouvez trouver de quelle répertoire belaran@999: il s'agit en lançant une fenêtre d'interpréteur de commande et en belaran@999: exécutant la commande suivante : belaran@999: belaran@999: C:\ echo belaran@999: %UserProfile belaran@999: belaran@999: belaran@999: # This is a Mercurial configuration file. belaran@999: [ui] belaran@999: username = Firstname Lastname <email.address@domain.net> belaran@999: belaran@999: La ligne avec [ui] commence une belaran@999: section du fichier de configuration, ainsi la ligne belaran@999: username = ... signifie belaran@999: définir la valeur de l'élément username dans la belaran@999: section ui. Une section continue jusqu'à ce belaran@999: qu'une nouvelle commence, ou que la fin du fichier soit atteinte. belaran@999: Mercurial ignore les lignes vides et traite tout texte situé à la suite belaran@999: d'un # jusqu'à la fin de la ligne belaran@999: comme un commentaire. belaran@999: belaran@999: belaran@999: belaran@999: Choisir un nom d'utilisateur belaran@999: belaran@999: Vous pouvez utiliser n'importe quelle valeur belaran@999: pour votre username, car cette information belaran@999: est destinée à d'autres personnes et non à être interprétée belaran@999: par Mercurial. La convention que la plupart des personnes belaran@999: suivent est d'utiliser leurs noms suivies de leurs adresses emails, belaran@999: comme montré ci-dessus : belaran@999: belaran@999: Le mécanisme interne du serveur web intégré à Mercurial, belaran@999: masque les adresses emails, pour rendre plus difficile leurs belaran@999: récupérations par les outils utilisés par les spammmers. belaran@999: Ceci réduit la probabilité que de recevoir encore plus de belaran@999: spam si vous vous publiez un dépôt sur internet. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Rédiger un message de \textit{commit} belaran@999: belaran@999: Lorsqu'on effectue une opération de commit, Mercurial belaran@999: lance automatiquement un éditeur de texte pour permettre de saisir belaran@999: un message qui décrira les modifications effectuées dans cette belaran@999: révision. Ce message est nommé le message de commit. belaran@999: Ce sera un enregistrement pour tout lecteur expliquant le pourquoi belaran@999: et le comment de vos modifications, et il sera affiché par la belaran@999: commande hg log. belaran@999: belaran@999: belaran@999: $ hg commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: L'éditeur que la commande hg belaran@999: commit déclenche ne contiendra qu'une ligne vide suivi belaran@999: d'un certain nombre de lignes commençant par HG: belaran@999: . belaran@999: belaran@999: belaran@999: This is where I type my commit comment. belaran@999: belaran@999: HG: Enter commit message. Lines beginning with 'HG:' are removed. belaran@999: HG: -- belaran@999: HG: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: HG: branch 'default' belaran@999: HG: changed hello.c belaran@999: belaran@999: belaran@999: Mercurial ignore les lignes qui commencent belaran@999: avec HG:, il ne les belaran@999: utilise que pour nous indiquer quels fichiers modifiés il se belaran@999: prépare à \textit{commiter}. Modifier ou effacer ces lignes n'a belaran@999: aucune conséquence sur l'opération de commit. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Rédiger un message \textit{approprié} belaran@999: belaran@999: Comme hg log n'affiche belaran@999: que la première ligne du message de commit par défaut, il est souvent belaran@999: considéré comme une bonne pratique de rédiger des messages de commit belaran@999: qui tiennent sur une seule ligne. Voilà un exemple concret de message belaran@999: de commit qui ne suit pas cette directive, et belaran@999: qui a donc un résumé peu lisible. belaran@999: belaran@999: belaran@999: changeset: 73:584af0e231be belaran@999: user: Censored Person <censored.person@example.org> belaran@999: date: Tue Sep 26 21:37:07 2006 -0700 belaran@999: summary: include buildmeister/commondefs. Add an exports and install belaran@999: belaran@999: belaran@999: A ce sujet, il faut noter qu'il n'existe pas de règle belaran@999: absolue dans ce domaine. Mercurial lui-même n'interprète pas les belaran@999: contenus des messages de commit, ainsi votre projet est libre de belaran@999: concevoir différentes politiques de mise en page des messages. belaran@999: belaran@999: Ma préférence personnelle va au message court, mais belaran@999: informatif, qui offre des précisions supplémentaires par rapport à ce belaran@999: que pourrait m'apprendre une commande hg log belaran@999: --patch. belaran@999: belaran@999: Si vous exécutez la commande hg belaran@999: commit sans aucun argument, elle enregistre tous les belaran@999: changements qui ont été fait, et qui sont indiqué par les commandes belaran@999: hg status et hg diff. belaran@999: belaran@999: belaran@999: Une surprise pour les utilisateurs habitués à Subversion belaran@999: belaran@999: Comme n'importe quel autre commande de Mercurial, si belaran@999: vous soumettez pas de manière explicite les noms des fichiers à belaran@999: committer à la commande hg commit, elle belaran@999: va travailler sur l'ensemble du répertoire de travail. Soyez conscient belaran@999: de ceci si vous venez du monde Subversion ou CVS, car vous pourriez belaran@999: attendre qu'elle opère uniquement le répertoire courant et ses sous belaran@999: répertoires. belaran@999: belaran@999: belaran@999: belaran@999: Annuler un \textit{commit} belaran@999: belaran@999: Si, en rédigeant le message, vous décidez que belaran@999: finalement vous ne voulez pas effectuer ce commit, il suffit belaran@999: de quitter simplement l'éditeur sans sauver. Ceci n'aura aucune belaran@999: conséquence sur le dépôt ou les fichiers du répertoire de belaran@999: travail. belaran@999: belaran@999: belaran@999: belaran@999: Admirer votre travail belaran@999: belaran@999: Une fois que votre \textit{commit} est terminé, vous belaran@999: pouvez utiliser la commande hg tip belaran@999: pour afficher le \textit{changeset} que vous venez de créer. Cette belaran@999: commande produit une sortie à l'écran qui est identique à celle du belaran@999: hg log, mais qui n'affiche que la belaran@999: dernière révision du dépôt. belaran@999: belaran@999: belaran@999: $ hg tip -vp belaran@999: changeset: 5:c94f208d1dfb belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: files: hello.c belaran@999: description: belaran@999: Added an extra line of output belaran@999: belaran@999: belaran@999: diff -r 2278160e78d4 -r c94f208d1dfb hello.c belaran@999: --- a/hello.c Sat Aug 16 22:16:53 2008 +0200 belaran@999: +++ b/hello.c Sun Aug 16 14:05:26 2009 +0000 belaran@999: @@ -8,5 +8,6 @@ belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: printf("hello, world!\"); belaran@999: + printf("hello again!\n"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: On fait couramment référence à la dernière révision belaran@999: du dépôt comme étant la révision tip, ou plus belaran@999: simplement le tip. belaran@999: belaran@999: Au passage, la commande hg belaran@999: tip accepte la plupart des options qu'accepte belaran@999: hg log. Ainsi ci dessus implique soit belaran@999: verbeux, belaran@999: veux dire affiche le patch. L'utilisation de l'option belaran@999: pour afficher un patch est un belaran@999: autre exemple de la cohérence des commandes évoquée plus tôt. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Partager ses modifications belaran@999: belaran@999: Nous avons mentionné plus haut que les dépôts belaran@999: de Mercurial sont autosuffisants. Ce qui signifie que la nouvelle belaran@999: révision que vous venez de créer existe seulement dans votre belaran@999: répertoire my-hello. Étudions belaran@999: comment propager cette modification dans d'autres dépôts. belaran@999: belaran@999: belaran@999: Récupérer les modifications d'autres dépôts belaran@999: belaran@999: Pour commencer, construisons un clone de notre dépôt belaran@999: hello qui ne contiendra pas belaran@999: le changement que nous venons d'effectuer. Nous l'appellerons notre belaran@999: dépôt temporaire hello-pull. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone hello hello-pull belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous allons utiliser la commande hg pull pour envoyer les modifications belaran@999: depuis my-hello dans hello-pull. Néanmoins, récupérer belaran@999: aveuglement des modifications depuis un dépôt a quelque chose d'un belaran@999: peu effrayant. Mercurial propose donc une commande hg incoming qui permet de savoir quelles belaran@999: modifications la commande hg pull belaran@999: pourrait entraîner dans notre dépôt, et ceci belaran@999: sans effectuer réellement de modification dessus. belaran@999: belaran@999: belaran@999: $ cd hello-pull belaran@999: $ hg incoming ../my-hello belaran@999: comparing with ../my-hello belaran@999: searching for changes belaran@999: changeset: 5:c94f208d1dfb belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Apporter les modifications rapatriées dans un dépôt se belaran@999: résume donc à exécuter la commande hg belaran@999: pull, et préciser depuis quel dépôt effectuer le hg pull. belaran@999: belaran@999: belaran@999: $ hg tip belaran@999: changeset: 4:2278160e78d4 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:16:53 2008 +0200 belaran@999: summary: Trim comments. belaran@999: belaran@999: $ hg pull ../my-hello belaran@999: pulling from ../my-hello belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: (run 'hg update' to get a working copy) belaran@999: $ hg tip belaran@999: changeset: 5:c94f208d1dfb belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Comme vous le voyez avec une sortie avant et après de la belaran@999: commande hg tip, nous avons réussi à belaran@999: récupérer aisément les modifications dans notre dépôt. Il reste néanmoins belaran@999: quelque chose à faire avant de placer ces modifications dans l'espace de belaran@999: travail. belaran@999: belaran@999: belaran@999: Récupérer des changements précis belaran@999: belaran@999: Il est possible à cause du délai entre l'exécution de la belaran@999: commande hg incoming et l'exécution de belaran@999: la commande hg pull, que vous ne belaran@999: puissiez pas voir toutes les modifications que vous rapporterez d'un belaran@999: autre dépôt. Supposons que vous récupériez les modifications d'un dépôt belaran@999: situé quelque part sur le réseau. Alors que vous regardez le résultat de belaran@999: la commande hg incoming, et avant que belaran@999: vous ne décidiez de récupérer ces modifications, quelqu'un peut ajouter belaran@999: de nouvelles révisions dans le dépôt distant. Ce qui signifie que vous belaran@999: récupérez plus de révision que ce que vous aviez regardées en utilisant belaran@999: la commande hg incoming. belaran@999: belaran@999: Si vous voulez seulement récupérer ce que vous aviez belaran@999: vérifier à l'aide de la commande hg belaran@999: incoming, ou que pour d'autres raisons vous souhaitiez ne belaran@999: récupérer qu'un sous ensemble des révisions supplémentaires belaran@999: disponibles, indiquant simplement les modifications que vous souhaitez belaran@999: récupérer par leurs ID de révision, soit hg pull belaran@999: -r7e95bb. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mise à jour de l'espace de travail belaran@999: belaran@999: Nous avons jusqu'à maintenant grossièrement défini la belaran@999: relation entre un dépôt et un espace de travail. La commande hg pull que nous avons exécutée dans la section belaran@999: a apporté des modifications, que nous belaran@999: avons vérifiées, dans notre dépôt, mais il n'y a aucune trace de ces belaran@999: modifications dans notre espace de travail. En effet, hg pull ne touche pas (par défaut) à l'espace belaran@999: de travail. C'est la commande hg update belaran@999: qui s'en charge. belaran@999: belaran@999: belaran@999: $ grep printf hello.c belaran@999: printf("hello, world!\"); belaran@999: $ hg update tip belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ grep printf hello.c belaran@999: printf("hello, world!\"); belaran@999: printf("hello again!\n"); belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Il peut sembler un peu étrange que la commande hg pull ne mette pas à jour l'espace de travail belaran@999: automatiquement. Il y a en fait une très bonne raison à cela : vous belaran@999: pouvez utilisez la commande hg update belaran@999: pour mettre à jour votre espace de travail à l'état dans lequel il était belaran@999: à n'importe quelle révision de l'historique du dépôt. belaran@999: Si vous aviez un espace de travail contenant une ancienne belaran@999: révision—pour chercher l'origine d'un bug, par exemple—et belaran@999: que vous effectuiez un hg pull qui belaran@999: mettrait à jour automatiquement votre espace de travail, vous ne seriez belaran@999: probablement pas très satisfait. belaran@999: belaran@999: Néanmoins, comme les opérations de pull sont très souvent belaran@999: suivies d'un update, Mercurial vous permet de combiner les belaran@999: deux aisément en passant l'option belaran@999: à la commande hg pull. belaran@999: belaran@999: 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 belaran@999: , vous pouvez constater qu'elle a belaran@999: affiché un rappel assez utile : vous devez encore effectuer une belaran@999: opération pour mettre à jour votre espace de travail. belaran@999: belaran@999: Pour découvrir sur quelle révision de l'espace de belaran@999: travail on se trouve, utilisez la commande hg belaran@999: parents. belaran@999: belaran@999: belaran@999: $ hg parents belaran@999: changeset: 5:c94f208d1dfb belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si vous regardez de nouveau le dessin , vous verrez les flèches reliant belaran@999: entre elles les révisions. Le nœud d'où la flèche belaran@999: part est dans chaque cas un parent, belaran@999: et le nœud où la flèche arrive est un belaran@999: enfant. belaran@999: belaran@999: Pour mettre à jour l'espace de travail d'une révision belaran@999: particulière, indiquez un numéro de révision ou un \textit{changeset belaran@999: ID} à la commande hg update. belaran@999: belaran@999: belaran@999: $ hg update 2 belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ hg parents belaran@999: changeset: 2:fef857204a0c belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sat Aug 16 22:05:04 2008 +0200 belaran@999: summary: Introduce a typo into hello.c. belaran@999: belaran@999: $ hg update belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ hg parents belaran@999: changeset: 5:c94f208d1dfb belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si vous ne précisez pas de manière explicite de numéro belaran@999: de révision la commande hg update belaran@999: mettra à jour votre espace de travail avec le contenu de la révison belaran@999: \textit{tip}, comme montré dans l'exemple ci dessus lors du second belaran@999: appel à hg update. belaran@999: belaran@999: belaran@999: belaran@999: Transférer les modifications vers un autre dépôt belaran@999: belaran@999: Mercurial vous laisse transférer les modifications vers belaran@999: un autre dépôt, depuis votre dépôt actuel. Comme dans l'exemple du belaran@999: hg pull ci-dessus, nous allons créer belaran@999: un dépôt temporaire vers lequel transférer nos modifications. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone hello hello-push belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La commande hg outgoing belaran@999: nous indique quels changements nous allons transférer vers l'autre belaran@999: serveur. belaran@999: belaran@999: belaran@999: $ cd my-hello belaran@999: $ hg outgoing ../hello-push belaran@999: comparing with ../hello-push belaran@999: searching for changes belaran@999: changeset: 5:c94f208d1dfb belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Et la commande hg push belaran@999: effectue réellement le transfert. belaran@999: belaran@999: belaran@999: $ hg push ../hello-push belaran@999: pushing to ../hello-push belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Comme avec hg pull, la belaran@999: commande hg push ne met pas à jour belaran@999: le répertoire de travail du dépôt dans lequel il transfère les belaran@999: modifications. À l'inverse de hg belaran@999: pull, hg push ne fournit belaran@999: pas d'option -u pour forcer la mise à jour de belaran@999: l'espace de travail cible. Cette asymétrie est délibéré : le dépot belaran@999: vers lequel nous transférons peut très bien être un serveur distant belaran@999: et partagé par plusieurs personnes. Si nous devions mettre à jour son belaran@999: répertoire de travail alors que quelqu'un d'autre travaille dessus, belaran@999: nous risquerions de perturber son travail. belaran@999: belaran@999: Qu'est ce qui se passe lorsque vous essayez de récupérer belaran@999: ou de transférer vos modifications et que le dépôt cible a déjà reçu belaran@999: ces modifications ? Rien de bien excitant. belaran@999: belaran@999: belaran@999: $ hg push ../hello-push belaran@999: pushing to ../hello-push belaran@999: searching for changes belaran@999: no changes found belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Emplacements par défaut belaran@999: belaran@999: Quand nous faisons un clone d'un dépôt, Mercurial belaran@999: enregistre l'emplacement du dépôt d'origine dans le fichier belaran@999: .hg/hgrc de notre nouveau dépôt. Si nous ne belaran@999: fournissons pas d'emplacement à la commande hg belaran@999: pull ou à la commande hg push, ces belaran@999: commandes utiliseront alors cet emplacement comme valeur par défaut. belaran@999: Les commandes hg incoming et hg belaran@999: outgoing feront de même. belaran@999: belaran@999: Si vous regardez le fichier belaran@999: .hg/hgrc, vous constaterez que son contenu belaran@999: ressemble à ce qui suit. belaran@999: belaran@999: [paths] belaran@999: default = http://www.selenic.com/repo/hg belaran@999: belaran@999: Il est possible—et souvent belaran@999: pratique—d'avoir un emplacement par défaut pour les commandes belaran@999: hg push et hg outgoing belaran@999: différent de celui des commandes hg pull et belaran@999: hg incoming. C'est faisable en ajoutant une entrée belaran@999: default-push à la section belaran@999: [paths] du .hg/hgrc, comme belaran@999: suit. belaran@999: belaran@999: [paths] belaran@999: default = http://www.selenic.com/repo/hg belaran@999: default-push = http://hg.example.com/hg belaran@999: belaran@999: belaran@999: belaran@999: Partager ses modifications à travers le réseau belaran@999: belaran@999: Les commandes que nous avons étudiées dans les sections belaran@999: précédentes ne sont pas limitées aux dépôts locaux. Chacune fonctionne belaran@999: de la même manière à travers une connexion réseau, il suffit de lui belaran@999: passer une URL à la place d'un chemin de fichier local. belaran@999: belaran@999: belaran@999: $ hg outgoing http://hg.serpentine.com/tutorial/hello belaran@999: comparing with http://hg.serpentine.com/tutorial/hello belaran@999: searching for changes belaran@999: changeset: 5:c94f208d1dfb belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Dans cet exemple, nous allons voir quels changements belaran@999: nous pourrions transférer vers le dépôt distant, mais le dépôt est, belaran@999: de manière tout à fait compréhensible, pas configuré pour accepter belaran@999: des modifications d'utilisateurs anonymes. belaran@999: belaran@999: belaran@999: $ hg push http://hg.serpentine.com/tutorial/hello belaran@999: pushing to http://hg.serpentine.com/tutorial/hello belaran@999: searching for changes belaran@999: ssl required belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Commencer un nouveau projet belaran@999: belaran@999: Il est tout aussi aisé de commencer un nouveau projet belaran@999: que de travailler sur un qui existe déjà. La commande hg belaran@999: init crée un nouveau dépôt Mercurial vide. belaran@999: belaran@999: belaran@999: $ hg init myproject belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Ceci crée simplement un répertoire nommé belaran@999: myproject dans le répertoire courant. belaran@999: belaran@999: belaran@999: $ ls -l belaran@999: total 12 belaran@999: -rw-r--r-- 1 rpelisse rpelisse 47 Aug 16 14:04 goodbye.c belaran@999: -rw-r--r-- 1 rpelisse rpelisse 45 Aug 16 14:04 hello.c belaran@999: drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:04 myproject belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous pouvons dire que myproject est belaran@999: un dépôt Mercurial car il contient un répertoire belaran@999: .hg. belaran@999: belaran@999: belaran@999: $ ls -al myproject belaran@999: total 12 belaran@999: drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:04 . belaran@999: drwx------ 3 rpelisse rpelisse 4096 Aug 16 14:04 .. belaran@999: drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:04 .hg belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si vous voulons ajouter quelques fichiers préexistants belaran@999: dans ce dépôt, il suffit de les recopier dans le répertoire de travail, belaran@999: et demander à Mercurial de commencer à les suivre en utilisant la belaran@999: commande hg add. belaran@999: belaran@999: belaran@999: $ cd myproject belaran@999: $ cp ../hello.c . belaran@999: $ cp ../goodbye.c . belaran@999: $ hg add belaran@999: adding goodbye.c belaran@999: adding hello.c belaran@999: $ hg status belaran@999: A goodbye.c belaran@999: A hello.c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Une fois que nous sommes satisfaits de notre projet, belaran@999: nous pouvons commencer à ajouter nos révisions. belaran@999: belaran@999: belaran@999: $ hg commit -m 'Initial commit' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Il ne prend que quelques instants pour commencer à belaran@999: utiliser Mercurial sur un nouveau projet, ce qui fait aussi de ses belaran@999: points forts. Travailler avec une gestion de révision devient très belaran@999: facile, nous pouvons même l'utiliser pour les plus petits projets où belaran@999: nous aurions probablement jamais penser utiliser un outils aussi belaran@999: complexe. belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Un rapide tour de Mercurial: fusionner les travaux belaran@999: belaran@999: Nous avons maintenant étudié comment cloner un dépôt, effectuer belaran@999: des changements dedans, et récupérer ou transférer depuis un belaran@999: autre dépôt. La prochaine étape est donc de fusionner les belaran@999: modifications de différents dépôts. belaran@999: belaran@999: belaran@999: Fusionner différents travaux belaran@999: La fusion est un aspect fondamental lorsqu'on belaran@999: travaille iavec un gestionnaire de source distribué. belaran@999: belaran@999: belaran@999: belaran@999: Alice et Bob ont chacun une copie personnelle du dépôt d'un belaran@999: projet sur lequel ils collaborent. Alice corrige un bug belaran@999: dans son dépôt, et Bob ajoute une nouvelle fonctionnalité dans le belaran@999: sien. Ils veulent un dépôt partagé avec à la fois le correctif du belaran@999: bug et la nouvelle fonctionnalité. belaran@999: belaran@999: belaran@999: Je travaille régulièrement sur plusieurs tâches différentes sur belaran@999: un seul projet en même temps, chacun isolé dans son propre dépôt. belaran@999: Travailler ainsi signifie que je dois régulièrement fusionner une belaran@999: partie de mon code avec celui des autres. belaran@999: belaran@999: belaran@999: belaran@999: Parce que la fusion est une opération si commune à réaliser, belaran@999: Mercurial la rend facile. Étudions ensemble le déroulement des belaran@999: opérations. Nous commencerons encore par faire un clone d'un autre belaran@999: dépôt (vous voyez que l'on fait ça tout le temps ?) puis nous ferons belaran@999: quelques modifications dessus. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone hello my-new-hello belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd my-new-hello belaran@999: # Make some simple edits to hello.c. belaran@999: $ my-text-editor hello.c belaran@999: $ hg commit -m 'A new hello for a new day.' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous devrions avoir maintenant deux copies de belaran@999: hello.c avec des contenus différents. Les belaran@999: historiques de ces deux dépôts ont aussi divergés, comme illustré dans belaran@999: la figure . belaran@999: belaran@999: belaran@999: $ cat hello.c belaran@999: /* belaran@999: * Placed in the public domain by Bryan O'Sullivan. This program is belaran@999: * not covered by patents in the United States or other countries. belaran@999: */ belaran@999: belaran@999: #include <stdio.h> belaran@999: belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: printf("once more, hello.\n"); belaran@999: printf("hello, world!\"); belaran@999: printf("hello again!\n"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Et ici est notre légèrement différente version du belaran@999: dépôt. belaran@999: belaran@999: belaran@999: $ cat ../my-hello/hello.c belaran@999: /* belaran@999: * Placed in the public domain by Bryan O'Sullivan. This program is belaran@999: * not covered by patents in the United States or other countries. belaran@999: */ belaran@999: belaran@999: #include <stdio.h> belaran@999: belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: printf("hello, world!\"); belaran@999: printf("hello again!\n"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999:
belaran@999: Historique divergent des dépôts <filename class="directory" moreinfo="none">my-hello</filename> et <filename class="directory" moreinfo="none">my-new-hello</filename>. belaran@999: belaran@999: belaran@999: XXX ajoute un test belaran@999: belaran@999:
belaran@999: belaran@999: Nous savons déjà que récupérer les modifications depuis belaran@999: notre dépôt my-hello n'aura belaran@999: aucun effet sur l'espace de travail. belaran@999: belaran@999: belaran@999: $ hg pull ../my-hello belaran@999: pulling from ../my-hello belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files (+1 heads) belaran@999: (run 'hg heads' to see heads, 'hg merge' to merge) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Néanmoins, la commande hg belaran@999: pull nous indique quelque chose au sujet des belaran@999: heads. belaran@999: belaran@999: belaran@999: Les révisions 'heads' belaran@999: belaran@999: Rappellez vous que Mercurial enregistre quelle révision belaran@999: est le parent de chaque révision. Si une révision a un parent, nous belaran@999: l'appelons un enfant(child) ou un descendant de ce parent. Une belaran@999: "head" est une révision qui n'a donc pas d'enfant. La révision tip belaran@999: est donc une "head", car c'est la révision la plus récente du dépôt belaran@999: qui n'a pas d'enfant. Il y a des moments où un dépôt peut contenir belaran@999: plusieurs "head". belaran@999: belaran@999:
belaran@999: Contenu du dépôt après une récupération ("pull") depuis le belaran@999: 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> belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: XXX ajoute un texte belaran@999: belaran@999:
belaran@999: belaran@999: Dans la figure , belaran@999: vous pouvez constater l'effet d'un \textit{pull} depuis le dépôt belaran@999: my-hello dans le dépôt belaran@999: my-new-hello. L'historique qui belaran@999: était déjà présent dans le dépôt my-new-hello reste intact, mais une belaran@999: nouvelle révision a été ajoutée. En vous reportant à la figure , vous pouvez voir que le belaran@999: ID de révision (changeset ID) reste le même dans belaran@999: le nouveau dépôt, mais que le numéro de belaran@999: révision reste le même. (Ceci est un parfait exemple de belaran@999: pourquoi il n'est fiable d'utiliser les numéros de révision lorsque belaran@999: l'on discute d'un \textit{changeset}.) Vous pouvez voir les "heads" belaran@999: présentes dans le dépôt en utilisant la commande hg heads. belaran@999: belaran@999: belaran@999: $ hg heads belaran@999: changeset: 6:c94f208d1dfb belaran@999: tag: tip belaran@999: parent: 4:2278160e78d4 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: changeset: 5:5f06f94fbeca belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:31 2009 +0000 belaran@999: summary: A new hello for a new day. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: Effectuer la fusion belaran@999: belaran@999: Que se passe-t-il quand vous essayez d'utiliser la belaran@999: commande hg update pour mettre à belaran@999: jour votre espace de travail au nouveau "tip" belaran@999: belaran@999: belaran@999: $ hg update belaran@999: abort: crosses branches (use 'hg merge' or 'hg update -C') belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial nous prévient que la commande hg update n'effectuera pas belaran@999: la fusion, il ne veut pas mettre à jour l'espace de travail quand il belaran@999: estime que nous pourrions avoir besoin d'une fusion, à moins de lui belaran@999: forcer la main. À la place, il faut utiliser la commande hg merge pour fusionner les deux belaran@999: "heads". belaran@999: belaran@999: Pour commencer une fusion (merge) entre deux "heads", belaran@999: nous utilisons la commande hg merge. belaran@999: belaran@999: belaran@999: $ hg merge belaran@999: merging hello.c belaran@999: 0 files updated, 1 files merged, 0 files removed, 0 files unresolved belaran@999: (branch merge, don't forget to commit) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous résolvons les conflits dans le fichier belaran@999: hello.c. Ceci met à jour le répertoire de travail belaran@999: de sorte qu'il ne contienne les modifications ne provenance des belaran@999: deux "heads", ce qui est indiqué par la belaran@999: la sortie de la commande hg belaran@999: parents et le contenu du fichier belaran@999: hello.c. belaran@999: belaran@999: belaran@999: $ hg parents belaran@999: changeset: 5:5f06f94fbeca belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:31 2009 +0000 belaran@999: summary: A new hello for a new day. belaran@999: belaran@999: changeset: 6:c94f208d1dfb belaran@999: tag: tip belaran@999: parent: 4:2278160e78d4 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:26 2009 +0000 belaran@999: summary: Added an extra line of output belaran@999: belaran@999: $ cat hello.c belaran@999: /* belaran@999: * Placed in the public domain by Bryan O'Sullivan. This program is belaran@999: * not covered by patents in the United States or other countries. belaran@999: */ belaran@999: belaran@999: #include <stdio.h> belaran@999: belaran@999: int main(int argc, char **argv) belaran@999: { belaran@999: printf("once more, hello.\n"); belaran@999: printf("hello, world!\"); belaran@999: printf("hello again!\n"); belaran@999: return 0; belaran@999: } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Effectuer l'ajout (commit) du résultat de la fusion belaran@999: belaran@999: Dès l'instant où vous avez effectué une fusion belaran@999: (merge), hg parents vous belaran@999: affichera deux parents, avant que vous n'exécutiez la commande belaran@999: hg commit sur le résultat de la belaran@999: fusion. belaran@999: belaran@999: belaran@999: $ hg commit -m 'Merged changes' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous avons maintenant un nouveau tip, remarquer qu'il belaran@999: contient à la fois nos anciennes "heads" et leurs belaran@999: parents. Ce sont les mêmes révisions que nous avions affichées avec belaran@999: la commande hg parents. belaran@999: belaran@999: belaran@999: $ hg tip belaran@999: changeset: 7:b8e1e756ef55 belaran@999: tag: tip belaran@999: parent: 5:5f06f94fbeca belaran@999: parent: 6:c94f208d1dfb belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:33 2009 +0000 belaran@999: summary: Merged changes belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Dans la figure , belaran@999: vous pouvez voir une représentation de ce qui se passe dans l'espace belaran@999: de travail pendant la fusion, et comment ceci affecte le dépôt lors belaran@999: du "commit". Pendant la fusion, l'espace de travail, qui a deux belaran@999: révisions (changesets) comme parents, voit ces derniers devenir le parent belaran@999: d'une nouvelle révision (changeset). belaran@999: belaran@999:
belaran@999: Working directory and repository during merge, and belaran@999: following commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: XXX ajoute texte belaran@999: belaran@999:
belaran@999: belaran@999:
belaran@999:
belaran@999: belaran@999: belaran@999: Fusionner les modifications en conflit belaran@999: belaran@999: La plupart des fusions sont assez simple à réaliser, mais belaran@999: parfois vous vous retrouverez à fusionner des fichiers où la modification belaran@999: touche la même portion de code, au sein d'un même fichier. À moins belaran@999: que ces modification ne soient identiques, ceci aboutira à un belaran@999: conflit, et vous devrez décider comment réconcilier belaran@999: les différentes modifications dans un tout cohérent. belaran@999: belaran@999:
belaran@999: Modifications en conflits dans un document belaran@999: belaran@999: belaran@999: XXX ajoute texte belaran@999: belaran@999:
belaran@999: belaran@999: La figure belaran@999: illustre un cas de modifications conflictuelles dans un document. Nous belaran@999: avons commencé avec une version simple de ce fichier, puis nous avons belaran@999: ajouté des modifications, pendant que quelqu'un d'autre modifiait le même belaran@999: texte. Notre tâche dans la résolution du conflit est de décider à quoi le belaran@999: fichier devrait ressembler. belaran@999: belaran@999: Mercurial n'a pas de mécanisme interne pour gérer belaran@999: les conflits. À la place, il exécute un programme externe appelé belaran@999: hgmerge. Il s'agit d'un script shell qui est belaran@999: embarqué par Mercurial, vous pouvez le modifier si vous le voulez. belaran@999: Ce qu'il fait par défaut est d'essayer de trouver un des différents belaran@999: outils de fusion qui seront probablement installés sur le système. belaran@999: Il commence par les outils totalement automatiques, et si ils belaran@999: échouent (parce que la résolution du conflit nécessite une belaran@999: intervention humaine) ou si ils sont absents, le script tente belaran@999: d'exécuter certains outils graphiques de fusion. belaran@999: belaran@999: Il est aussi possible de demander à Mercurial d'exécuter belaran@999: un autre programme ou un autre script en définissant la variable belaran@999: d'environnement HGMERGE avec le nom belaran@999: du programme de votre choix. belaran@999: belaran@999: belaran@999: Utiliser un outil graphique de fusion belaran@999: belaran@999: Mon outil de fusion préféré est belaran@999: kdiff3, que j'utilise ici pour illustrer les belaran@999: fonctionnalités classiques des outils graphiques de fusion. Vous pouvez belaran@999: voir une capture d'écran de l'utilisation de kdiff3 belaran@999: dans la figure . Cet outil belaran@999: effectue une fusion \textit{three-way}, car il y a belaran@999: trois différentes versions du fichier qui nous intéresse. Le fichier belaran@999: découpe la partie supérieure de la fenêtre en trois panneaux: belaran@999: belaran@999: A gauche on la version de belaran@999: base du fichier, soit la plus récente version belaran@999: des deux versions qu'on souhaite fusionner. belaran@999: Au centre, il y a notre belaran@999: version du fichier, avec le contenu que nous avons modifié. belaran@999: Sur la droite, on trouve belaran@999: leur version du fichier, celui qui contient la belaran@999: révision que nous souhaitons intégré. belaran@999: belaran@999: Dans le panneau en dessous, on trouve le belaran@999: résultat actuel de notre fusion. Notre tâche belaran@999: consiste donc à remplacement tous les textes en rouges, belaran@999: qui indiquent des conflits non résolus, avec une fusion manuelle et belaran@999: pertinente de notre version et de la leur. belaran@999: belaran@999: belaran@999: Tous les quatre panneaux sont accrochés ensemble, belaran@999: si nous déroulons les ascenseurs verticalement ou horizontalement dans chacun belaran@999: d'entre eux, les autres sont mis à jour avec la section correspondante dans leurs belaran@999: fichiers respectifs. belaran@999: belaran@999:
belaran@999: Utiliser <command moreinfo="none">kdiff3</command> pour fusionner les belaran@999: différentes version d'un fichier. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: XXX ajoute texte belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: Pour chaque portion de fichier posant problème, nous belaran@999: pouvons choisir de résoudre le conflit en utilisant une combinaison de belaran@999: texte depuis la version de base, la notre, ou la leur. Nous pouvons belaran@999: aussi éditer manuellement les fichiers à tout moment, si c'est nécessaire. belaran@999: belaran@999: Il y a beaucoup d'outils de belaran@999: fusion disponibles, bien trop pour en parler de tous ici. Leurs belaran@999: disponibilités varient selon les plate formes ainsi que leurs belaran@999: avantages et inconvénients. La plupart sont optimisé pour belaran@999: la fusion de fichier contenant un texte plat, certains sont spécialisé belaran@999: dans un format de fichier précis (généralement XML). belaran@999:
belaran@999: belaran@999: belaran@999: Un exemple concret belaran@999: belaran@999: Dans cet exemple, nous allons reproduire la belaran@999: modification de l'historique du fichier de la figure ci dessus. Commençons par créer belaran@999: un dépôt avec une version de base de notre document. belaran@999: belaran@999: belaran@999: $ cat > letter.txt <<EOF belaran@999: > Greetings! belaran@999: > I am Mariam Abacha, the wife of former belaran@999: > Nigerian dictator Sani Abacha. belaran@999: > EOF belaran@999: $ hg add letter.txt belaran@999: $ hg commit -m '419 scam, first draft' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Créons un clone de ce dépôt et faisons une belaran@999: modification dans le fichier. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone scam scam-cousin belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd scam-cousin belaran@999: $ cat > letter.txt <<EOF belaran@999: > Greetings! belaran@999: > I am Shehu Musa Abacha, cousin to the former belaran@999: > Nigerian dictator Sani Abacha. belaran@999: > EOF belaran@999: $ hg commit -m '419 scam, with cousin' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Et un autre clone, pour simuler que quelqu'un d'autre effectue une belaran@999: modification sur le fichier. (Ceci pour suggérer qu'il n'est pas rare belaran@999: de devoir effectuer des fusions (merges) avec vos propres travaux quand belaran@999: vous isolez les tâches dans des dépôts distincts. En effet, vous belaran@999: aurez alors à trouver et résoudre certains conflits). belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone scam scam-son belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd scam-son belaran@999: $ cat > letter.txt <<EOF belaran@999: > Greetings! belaran@999: > I am Alhaji Abba Abacha, son of the former belaran@999: > Nigerian dictator Sani Abacha. belaran@999: > EOF belaran@999: $ hg commit -m '419 scam, with son' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Maintenant que ces deux versions différentes du même fichier sont belaran@999: créées, nous allons configurer l'environnement de manière appropriée pour belaran@999: exécuter notre fusion (merge). belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone scam-cousin scam-merge belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd scam-merge belaran@999: $ hg pull -u ../scam-son belaran@999: pulling from ../scam-son belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files (+1 heads) belaran@999: not updating, since new heads added belaran@999: (run 'hg heads' to see heads, 'hg merge' to merge) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Dans cette exemple, je n'utiliserais pas la commande Mercurial belaran@999: habituelle hgmerge pour effectuer le belaran@999: fusion (merge), car il me faudrait abandonner ce joli petit exemple automatisé belaran@999: pour utiliser un outil graphique. À la place, je vais définir la belaran@999: variable d'environnement HGMERGE pour indiquer à belaran@999: Mercurial d'utiliser la commande non-interactive merge. belaran@999: Cette dernière est embarquée par de nombreux systèmes à la Unix. belaran@999: Si vous exécutez cet exemple depuis votre ordinateur, ne vous belaran@999: occupez pas de définir HGMERGE. belaran@999: belaran@999: belaran@999: $ export HGMERGE=merge belaran@999: $ hg merge belaran@999: merging letter.txt belaran@999: merge: warning: conflicts during merge belaran@999: merging letter.txt failed! belaran@999: 0 files updated, 0 files merged, 0 files removed, 1 files unresolved belaran@999: use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon belaran@999: $ cat letter.txt belaran@999: Greetings! belaran@999: <<<<<<< /tmp/tour-merge-conflictk3twLJ/scam-merge/letter.txt belaran@999: I am Shehu Musa Abacha, cousin to the former belaran@999: ======= belaran@999: I am Alhaji Abba Abacha, son of the former belaran@999: >>>>>>> /tmp/letter.txt~other.4O623C belaran@999: Nigerian dictator Sani Abacha. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Parce que merge ne peut pas résoudre belaran@999: les modifications conflictuelles, il laisse des marqueurs de belaran@999: différences à l'intérieur du fichier qui a des conflits, belaran@999: indiquant clairement quelles lignes sont en conflits, et si elles belaran@999: viennent de notre fichier ou du fichier externe. belaran@999: belaran@999: belaran@999: Mercurial peut distinguer, à la manière dont la belaran@999: commande merge se termine, qu'elle n'a pas été belaran@999: capable d'effectuer la fusion (merge), alors il nous indique que nous belaran@999: devons effectuer de nouveau cette opération. Ceci peut être très utile belaran@999: si, par exemple, nous exécutons un outil graphique de fusion et que belaran@999: nous le quittons sans nous rendre compte qu'il reste des conflits ou belaran@999: simplement par erreur. belaran@999: belaran@999: Si la fusion (merge) automatique ou manuelle échoue, belaran@999: il n'y a rien pour nous empêcher de corriger le tir en belaran@999: modifiant nous même les fichiers, et enfin effectuer le "commit" du belaran@999: fichier: belaran@999: belaran@999: belaran@999: $ cat > letter.txt <<EOF belaran@999: > Greetings! belaran@999: > I am Bryan O'Sullivan, no relation of the former belaran@999: > Nigerian dictator Sani Abacha. belaran@999: > EOF belaran@999: $ hg resolve -m letter.txt belaran@999: $ hg commit -m 'Send me your money' belaran@999: $ hg tip belaran@999: changeset: 3:0954bda76c6b belaran@999: tag: tip belaran@999: parent: 1:1ac156b6e708 belaran@999: parent: 2:7ee20631b33b belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:34 2009 +0000 belaran@999: summary: Send me your money belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Où est la <command moreinfo="none">hg resolve</command> ? belaran@999: belaran@999: La commande hg resolve a été belaran@999: introduit dans la version 1.1 de Mercurial, qui a été publié en belaran@999: décembre 2008. Si vous utilisez une version plus anciennne de belaran@999: Mercurial (exécutez la command hg version pour en belaran@999: avoir le coeur net), cette commande ne sera pas disponible. Si votre belaran@999: version de Mercurial est plus ancienne que la 1.1, vous devriez très belaran@999: fortement considérer une mise à jour à une version plus récente avant belaran@999: d'essayer de régler des fusions complexes. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: Simplification de la séquence pull-merge-commit belaran@999: belaran@999: La procédure pour effectuer la fusion indiquée belaran@999: ci-dessus est simple, mais requiert le lancement de trois commandes à la belaran@999: suite. belaran@999: belaran@999: hg pull -u belaran@999: hg merge belaran@999: hg commit -m 'Merged remote changes' belaran@999: belaran@999: Lors du "commit" final, vous devez également saisir un belaran@999: message, qui aura vraisemblablement assez peu d'intérêt. belaran@999: belaran@999: Il serait assez sympathique de pouvoir réduire le belaran@999: nombre d'opérations nécessaire, si possible. De fait Mercurial est belaran@999: fourni avec une extension appelé fetch belaran@999: qui fait justement cela. belaran@999: belaran@999: Mercurial fourni un mécanisme d'extension flexible qui permet à chacun belaran@999: d'étendre ces fonctionnalités, tout en conservant le cœur de Mercurial belaran@999: léger et facile à utiliser. Certains extensions ajoutent de nouvelles belaran@999: commandes que vous pouvez utiliser en ligne de commande, alors que belaran@999: d'autres travaillent en coulisse, par exemple en ajoutant des belaran@999: possibilités au serveur. belaran@999: belaran@999: L'extension fetch belaran@999: ajoute une nouvelle commande nommée, sans surprise, hg fetch. Cette extension résulte en une belaran@999: combinaison de hg pull, hg update and hg belaran@999: merge. Elle commence par récupérer les modifications d'un belaran@999: autre dépôt dans le dépôt courant. Si elle trouve que les belaran@999: modifications ajoutent une nouvelle "head", elle effectue un "merge", belaran@999: et ensuite "commit" le résultat du "merge" avec un message généré belaran@999: automatiquement. Si aucune "head" n'ont été ajouté, elle met à jour le belaran@999: répertoire de travail au niveau de la nouvelle révision tip. belaran@999: belaran@999: Activer l'extension fetch est facile. Modifiez votre .hgrc, et soit allez à la section extensions soit créer une section belaran@999: extensions. Ensuite ajoutez belaran@999: une ligne qui consiste simplement en \Verb+fetch =. belaran@999: belaran@999: [extensions] belaran@999: fetch = belaran@999: belaran@999: (Normalement, sur la partie droite de belaran@999: = devrait apparaître le chemin de belaran@999: l'extension, mais étant donné que l'extension fetch fait partie de la distribution standard, belaran@999: Mercurial sait où la trouver.) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Renommer, copier, et fusionner (merge) belaran@999: belaran@999: En cours de la vie d'un projet, nous allons souvent belaran@999: vouloir changer la disposition de ses fichiers et de ses répertoires. belaran@999: Ceci peut être aussi simple que de changer le nom d'un seul fichier, belaran@999: et aussi compliqué que de restructurer une hiérarchie entiere de fichier belaran@999: au sein du projet. belaran@999: belaran@999: Mercurial permet de faire ce genre de modification de belaran@999: manière fluide, à condition de l'informer de ce que nous faisons. Si belaran@999: vous voulez renommenr un ficher, vous devriez utiliser les commande belaran@999: hg rename belaran@999: Si vous un utilisateur de Unix, vous serez content belaran@999: de savoir que la commande hg rename command belaran@999: peut être abrégée en hg mv. belaran@999: pour changer son nom, ainsi Mercurial peut ensuite prendre belaran@999: la bonne décision, plus tard, en cas de fusionv (merge). belaran@999: belaran@999: Nous étudierojns en détail l'utilisation de ces commandes, belaran@999: en détail, dans le chapitre . belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Derrière le décor belaran@999: belaran@999: À la différence de beaucoup d'outils de gestion de versions, belaran@999: les concepts sur lesquels se base Mercurial sont assez simples pour belaran@999: qu'il soit facile de comprendre comment le logiciel fonctionne. belaran@999: Bien que leur connaissance ne soit pas nécéssaire, je trouve utile belaran@999: d'avoir un modèle mental de ce qui se passe. belaran@999: belaran@999: En effet, cette compréhension m'apporte la confiance que belaran@999: Mercurial a été développé avec soin pour être à la fois belaran@999: sûr et efficace. De surcroît, belaran@999: si il m'est facile de garder en tête ce que le logiciel fait lorsque belaran@999: j'accompli des tâches de révision, j'aurai moins de risques d'être belaran@999: surpris par son comportement. belaran@999: belaran@999: Dans ce chapitre, nous décrirons tout d'abord les concepts belaran@999: essentiels de l'architecture de Mercurial, pour ensuite discuter quelques belaran@999: uns des détails intéressants de son implémentation. belaran@999: belaran@999: belaran@999: Conservation de l'historique sous Mercurial belaran@999: belaran@999: Suivi de l'historique pour un seul fichier belaran@999: belaran@999: Lorsque Mercurial effectue un suivi des modifications belaran@999: faites à un fichier, il conserve l'historique pour ce fichier dans un belaran@999: filelog sous forme de métadonnées. Chaque entrée belaran@999: dans le filelog contient assez d'informations pour reconstituer une belaran@999: révision du fichier correspondant. Les filelogs sont des fichiers belaran@999: stockés dans le répertoire .hg/store/data. Un filelog contient belaran@999: des informations de deux types: les données de révision, et un index belaran@999: pour permettre à Mercurial une recherche efficace d'une révision belaran@999: donnée. belaran@999: belaran@999: Lorsqu'un fichier devient trop gros ou a un long belaran@999: historique, son filelog se voit stocker dans un fichier de données belaran@999: (avec un suffixe .d) et un fichier belaran@999: index (avec un suffixe.i) belaran@999: distincts. La relation entre un fichier dans le répertoire de travail belaran@999: et le filelog couvrant le suivi de son historique dans le dépôt est belaran@999: illustré à la figure . belaran@999: belaran@999:
belaran@999: Relations entre les fichiers dans le répertoire de travail et belaran@999: leurs filelogs dans le dépôt belaran@999: belaran@999: XXX add text belaran@999:
belaran@999: belaran@999:
belaran@999: belaran@999: Gestion des fichiers suivis belaran@999: belaran@999: Mercurial a recours à une structure nommée belaran@999: manifest pour rassembler les informations sur belaran@999: les fichiers dont il gère le suivi. Chaque entrée dans ce manifest belaran@999: contient des informations sur les fichiers présents dans une révision belaran@999: donnée. Une entrée store la liste des fichiers faisant partie de la belaran@999: révision, la version de chaque fichier, et quelques autres belaran@999: métadonnées sur ces fichiers. belaran@999: belaran@999: belaran@999: belaran@999: Recording changeset information belaran@999: belaran@999: The changelog contains belaran@999: information about each changeset. Each revision records who belaran@999: committed a change, the changeset comment, other pieces of belaran@999: changeset-related information, and the revision of the manifest to belaran@999: use. belaran@999: belaran@999: belaran@999: belaran@999: Relationships between revisions belaran@999: belaran@999: Within a changelog, a manifest, or a filelog, each belaran@999: revision stores a pointer to its immediate parent (or to its belaran@999: two parents, if it's a merge revision). As I mentioned above, belaran@999: there are also relationships between revisions belaran@999: across these structures, and they are belaran@999: hierarchical in nature. belaran@999: belaran@999: For every changeset in a repository, there is exactly one belaran@999: revision stored in the changelog. Each revision of the belaran@999: changelog contains a pointer to a single revision of the belaran@999: manifest. A revision of the manifest stores a pointer to a belaran@999: single revision of each filelog tracked when that changeset belaran@999: was created. These relationships are illustrated in belaran@999: . belaran@999: belaran@999:
belaran@999: Metadata relationships belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: As the illustration shows, there is belaran@999: not a one to one belaran@999: relationship between revisions in the changelog, manifest, or belaran@999: filelog. If a file that belaran@999: Mercurial tracks hasn't changed between two changesets, the belaran@999: entry for that file in the two revisions of the manifest will belaran@999: point to the same revision of its filelog belaran@999: It is possible (though unusual) for the manifest to belaran@999: remain the same between two changesets, in which case the belaran@999: changelog entries for those changesets will point to the belaran@999: same revision of the manifest. belaran@999: . belaran@999: belaran@999:
belaran@999:
belaran@999: belaran@999: Safe, efficient storage belaran@999: belaran@999: The underpinnings of changelogs, manifests, and filelogs are belaran@999: provided by a single structure called the belaran@999: revlog. belaran@999: belaran@999: belaran@999: Efficient storage belaran@999: belaran@999: The revlog provides efficient storage of revisions using a belaran@999: delta mechanism. Instead of storing a belaran@999: complete copy of a file for each revision, it stores the belaran@999: changes needed to transform an older revision into the new belaran@999: revision. For many kinds of file data, these deltas are belaran@999: typically a fraction of a percent of the size of a full copy belaran@999: of a file. belaran@999: belaran@999: Some obsolete revision control systems can only work with belaran@999: deltas of text files. They must either store binary files as belaran@999: complete snapshots or encoded into a text representation, both belaran@999: of which are wasteful approaches. Mercurial can efficiently belaran@999: handle deltas of files with arbitrary binary contents; it belaran@999: doesn't need to treat text as special. belaran@999: belaran@999: belaran@999: belaran@999: Safe operation belaran@999: belaran@999: Mercurial only ever appends data to belaran@999: the end of a revlog file. It never modifies a section of a belaran@999: file after it has written it. This is both more robust and belaran@999: efficient than schemes that need to modify or rewrite belaran@999: data. belaran@999: belaran@999: In addition, Mercurial treats every write as part of a belaran@999: transaction that can span a number of belaran@999: files. A transaction is atomic: either belaran@999: the entire transaction succeeds and its effects are all belaran@999: visible to readers in one go, or the whole thing is undone. belaran@999: This guarantee of atomicity means that if you're running two belaran@999: copies of Mercurial, where one is reading data and one is belaran@999: writing it, the reader will never see a partially written belaran@999: result that might confuse it. belaran@999: belaran@999: The fact that Mercurial only appends to files makes it belaran@999: easier to provide this transactional guarantee. The easier it belaran@999: is to do stuff like this, the more confident you should be belaran@999: that it's done correctly. belaran@999: belaran@999: belaran@999: belaran@999: Fast retrieval belaran@999: belaran@999: Mercurial cleverly avoids a pitfall common to belaran@999: all earlier revision control systems: the problem of belaran@999: inefficient retrieval. Most revision belaran@999: control systems store the contents of a revision as an belaran@999: incremental series of modifications against a belaran@999: snapshot. (Some base the snapshot on the belaran@999: oldest revision, others on the newest.) To reconstruct a belaran@999: specific revision, you must first read the snapshot, and then belaran@999: every one of the revisions between the snapshot and your belaran@999: target revision. The more history that a file accumulates, belaran@999: the more revisions you must read, hence the longer it takes to belaran@999: reconstruct a particular revision. belaran@999: belaran@999:
belaran@999: Snapshot of a revlog, with incremental deltas belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: The innovation that Mercurial applies to this problem is belaran@999: simple but effective. Once the cumulative amount of delta belaran@999: information stored since the last snapshot exceeds a fixed belaran@999: threshold, it stores a new snapshot (compressed, of course), belaran@999: instead of another delta. This makes it possible to belaran@999: reconstruct any revision of a file belaran@999: quickly. This approach works so well that it has since been belaran@999: copied by several other revision control systems. belaran@999: belaran@999: illustrates belaran@999: the idea. In an entry in a revlog's index file, Mercurial belaran@999: stores the range of entries from the data file that it must belaran@999: read to reconstruct a particular revision. belaran@999: belaran@999: belaran@999: Aside: the influence of video compression belaran@999: belaran@999: If you're familiar with video compression or belaran@999: have ever watched a TV feed through a digital cable or belaran@999: satellite service, you may know that most video compression belaran@999: schemes store each frame of video as a delta against its belaran@999: predecessor frame. belaran@999: belaran@999: Mercurial borrows this idea to make it belaran@999: possible to reconstruct a revision from a snapshot and a belaran@999: small number of deltas. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: Identification and strong integrity belaran@999: belaran@999: Along with delta or snapshot information, a revlog entry belaran@999: contains a cryptographic hash of the data that it represents. belaran@999: This makes it difficult to forge the contents of a revision, belaran@999: and easy to detect accidental corruption. belaran@999: belaran@999: Hashes provide more than a mere check against corruption; belaran@999: they are used as the identifiers for revisions. The changeset belaran@999: identification hashes that you see as an end user are from belaran@999: revisions of the changelog. Although filelogs and the belaran@999: manifest also use hashes, Mercurial only uses these behind the belaran@999: scenes. belaran@999: belaran@999: Mercurial verifies that hashes are correct when it belaran@999: retrieves file revisions and when it pulls changes from belaran@999: another repository. If it encounters an integrity problem, it belaran@999: will complain and stop whatever it's doing. belaran@999: belaran@999: In addition to the effect it has on retrieval efficiency, belaran@999: Mercurial's use of periodic snapshots makes it more robust belaran@999: against partial data corruption. If a revlog becomes partly belaran@999: corrupted due to a hardware error or system bug, it's often belaran@999: possible to reconstruct some or most revisions from the belaran@999: uncorrupted sections of the revlog, both before and after the belaran@999: corrupted section. This would not be possible with a belaran@999: delta-only storage model. belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: Revision history, branching, and merging belaran@999: belaran@999: Every entry in a Mercurial revlog knows the identity of its belaran@999: immediate ancestor revision, usually referred to as its belaran@999: parent. In fact, a revision contains room belaran@999: for not one parent, but two. Mercurial uses a special hash, belaran@999: called the null ID, to represent the idea belaran@999: there is no parent here. This hash is simply a belaran@999: string of zeroes. belaran@999: belaran@999: In , you can see belaran@999: an example of the conceptual structure of a revlog. Filelogs, belaran@999: manifests, and changelogs all have this same structure; they belaran@999: differ only in the kind of data stored in each delta or belaran@999: snapshot. belaran@999: belaran@999: The first revision in a revlog (at the bottom of the image) belaran@999: has the null ID in both of its parent slots. For a belaran@999: normal revision, its first parent slot contains belaran@999: the ID of its parent revision, and its second contains the null belaran@999: ID, indicating that the revision has only one real parent. Any belaran@999: two revisions that have the same parent ID are branches. A belaran@999: revision that represents a merge between branches has two normal belaran@999: revision IDs in its parent slots. belaran@999: belaran@999:
belaran@999: The conceptual structure of a revlog belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999:
belaran@999: belaran@999: The working directory belaran@999: belaran@999: In the working directory, Mercurial stores a snapshot of the belaran@999: files from the repository as of a particular changeset. belaran@999: belaran@999: The working directory knows which changeset belaran@999: it contains. When you update the working directory to contain a belaran@999: particular changeset, Mercurial looks up the appropriate belaran@999: revision of the manifest to find out which files it was tracking belaran@999: at the time that changeset was committed, and which revision of belaran@999: each file was then current. It then recreates a copy of each of belaran@999: those files, with the same contents it had when the changeset belaran@999: was committed. belaran@999: belaran@999: The dirstate is a special belaran@999: structure that contains Mercurial's knowledge of the working belaran@999: directory. It is maintained as a file named belaran@999: .hg/dirstate inside a repository. The belaran@999: dirstate details which changeset the working directory is belaran@999: updated to, and all of the files that Mercurial is tracking in belaran@999: the working directory. It also lets Mercurial quickly notice belaran@999: changed files, by recording their checkout times and belaran@999: sizes. belaran@999: belaran@999: Just as a revision of a revlog has room for two parents, so belaran@999: that it can represent either a normal revision (with one parent) belaran@999: or a merge of two earlier revisions, the dirstate has slots for belaran@999: two parents. When you use the hg belaran@999: update command, the changeset that you update to is belaran@999: stored in the first parent slot, and the null ID belaran@999: in the second. When you hg belaran@999: merge with another changeset, the first parent belaran@999: remains unchanged, and the second parent is filled in with the belaran@999: changeset you're merging with. The hg belaran@999: parents command tells you what the parents of the belaran@999: dirstate are. belaran@999: belaran@999: belaran@999: What happens when you commit belaran@999: belaran@999: The dirstate stores parent information for more than just belaran@999: book-keeping purposes. Mercurial uses the parents of the belaran@999: dirstate as the parents of a new belaran@999: changeset when you perform a commit. belaran@999: belaran@999:
belaran@999: The working directory can have two parents belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: shows the belaran@999: normal state of the working directory, where it has a single belaran@999: changeset as parent. That changeset is the belaran@999: tip, the newest changeset in the belaran@999: repository that has no children. belaran@999: belaran@999:
belaran@999: The working directory gains new parents after a belaran@999: commit belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: It's useful to think of the working directory as belaran@999: the changeset I'm about to commit. Any files belaran@999: that you tell Mercurial that you've added, removed, renamed, belaran@999: or copied will be reflected in that changeset, as will belaran@999: modifications to any files that Mercurial is already tracking; belaran@999: the new changeset will have the parents of the working belaran@999: directory as its parents. belaran@999: belaran@999: After a commit, Mercurial will update the belaran@999: parents of the working directory, so that the first parent is belaran@999: the ID of the new changeset, and the second is the null ID. belaran@999: This is shown in . Mercurial belaran@999: doesn't touch any of the files in the working directory when belaran@999: you commit; it just modifies the dirstate to note its new belaran@999: parents. belaran@999: belaran@999:
belaran@999: belaran@999: Creating a new head belaran@999: belaran@999: It's perfectly normal to update the working directory to a belaran@999: changeset other than the current tip. For example, you might belaran@999: want to know what your project looked like last Tuesday, or belaran@999: you could be looking through changesets to see which one belaran@999: introduced a bug. In cases like this, the natural thing to do belaran@999: is update the working directory to the changeset you're belaran@999: interested in, and then examine the files in the working belaran@999: directory directly to see their contents as they were when you belaran@999: committed that changeset. The effect of this is shown in belaran@999: . belaran@999: belaran@999:
belaran@999: The working directory, updated to an older belaran@999: changeset belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: Having updated the working directory to an belaran@999: older changeset, what happens if you make some changes, and belaran@999: then commit? Mercurial behaves in the same way as I outlined belaran@999: above. The parents of the working directory become the belaran@999: parents of the new changeset. This new changeset has no belaran@999: children, so it becomes the new tip. And the repository now belaran@999: contains two changesets that have no children; we call these belaran@999: heads. You can see the structure that belaran@999: this creates in . belaran@999: belaran@999:
belaran@999: After a commit made while synced to an older belaran@999: changeset belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: If you're new to Mercurial, you should keep belaran@999: in mind a common error, which is to use the belaran@999: hg pull command without any belaran@999: options. By default, the hg belaran@999: pull command does not belaran@999: update the working directory, so you'll bring new changesets belaran@999: into your repository, but the working directory will stay belaran@999: synced at the same changeset as before the pull. If you belaran@999: make some changes and commit afterwards, you'll thus create belaran@999: a new head, because your working directory isn't synced to belaran@999: whatever the current tip is. To combine the operation of a belaran@999: pull, followed by an update, run hg pull belaran@999: -u. belaran@999: belaran@999: I put the word error in quotes belaran@999: because all that you need to do to rectify the situation belaran@999: where you created a new head by accident is belaran@999: hg merge, then hg commit. In other words, this belaran@999: almost never has negative consequences; it's just something belaran@999: of a surprise for newcomers. I'll discuss other ways to belaran@999: avoid this behavior, and why Mercurial behaves in this belaran@999: initially surprising way, later on. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: Merging changes belaran@999: belaran@999: When you run the hg belaran@999: merge command, Mercurial leaves the first parent belaran@999: of the working directory unchanged, and sets the second parent belaran@999: to the changeset you're merging with, as shown in . belaran@999: belaran@999:
belaran@999: Merging two heads belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: Mercurial also has to modify the working directory, to belaran@999: merge the files managed in the two changesets. Simplified a belaran@999: little, the merging process goes like this, for every file in belaran@999: the manifests of both changesets. belaran@999: belaran@999: If neither changeset has modified a file, do belaran@999: nothing with that file. belaran@999: belaran@999: If one changeset has modified a file, and the belaran@999: other hasn't, create the modified copy of the file in the belaran@999: working directory. belaran@999: belaran@999: If one changeset has removed a file, and the belaran@999: other hasn't (or has also deleted it), delete the file belaran@999: from the working directory. belaran@999: belaran@999: If one changeset has removed a file, but the belaran@999: other has modified the file, ask the user what to do: keep belaran@999: the modified file, or remove it? belaran@999: belaran@999: If both changesets have modified a file, belaran@999: invoke an external merge program to choose the new belaran@999: contents for the merged file. This may require input from belaran@999: the user. belaran@999: belaran@999: If one changeset has modified a file, and the belaran@999: other has renamed or copied the file, make sure that the belaran@999: changes follow the new name of the file. belaran@999: belaran@999: There are more details—merging has plenty of corner belaran@999: cases—but these are the most common choices that are belaran@999: involved in a merge. As you can see, most cases are belaran@999: completely automatic, and indeed most merges finish belaran@999: automatically, without requiring your input to resolve any belaran@999: conflicts. belaran@999: belaran@999: When you're thinking about what happens when you commit belaran@999: after a merge, once again the working directory is the belaran@999: changeset I'm about to commit. After the hg merge command completes, the belaran@999: working directory has two parents; these will become the belaran@999: parents of the new changeset. belaran@999: belaran@999: Mercurial lets you perform multiple merges, but belaran@999: you must commit the results of each individual merge as you belaran@999: go. This is necessary because Mercurial only tracks two belaran@999: parents for both revisions and the working directory. While belaran@999: it would be technically feasible to merge multiple changesets belaran@999: at once, Mercurial avoids this for simplicity. With multi-way belaran@999: merges, the risks of user confusion, nasty conflict belaran@999: resolution, and making a terrible mess of a merge would grow belaran@999: intolerable. belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: Merging and renames belaran@999: belaran@999: A surprising number of revision control systems pay little belaran@999: or no attention to a file's name over belaran@999: time. For instance, it used to be common that if a file got belaran@999: renamed on one side of a merge, the changes from the other belaran@999: side would be silently dropped. belaran@999: belaran@999: Mercurial records metadata when you tell it to perform a belaran@999: rename or copy. It uses this metadata during a merge to do the belaran@999: right thing in the case of a merge. For instance, if I rename belaran@999: a file, and you edit it without renaming it, when we merge our belaran@999: work the file will be renamed and have your edits belaran@999: applied. belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: Other interesting design features belaran@999: belaran@999: In the sections above, I've tried to highlight some of the belaran@999: most important aspects of Mercurial's design, to illustrate that belaran@999: it pays careful attention to reliability and performance. belaran@999: However, the attention to detail doesn't stop there. There are belaran@999: a number of other aspects of Mercurial's construction that I belaran@999: personally find interesting. I'll detail a few of them here, belaran@999: separate from the big ticket items above, so that belaran@999: if you're interested, you can gain a better idea of the amount belaran@999: of thinking that goes into a well-designed system. belaran@999: belaran@999: belaran@999: Clever compression belaran@999: belaran@999: When appropriate, Mercurial will store both snapshots and belaran@999: deltas in compressed form. It does this by always belaran@999: trying to compress a snapshot or delta, belaran@999: but only storing the compressed version if it's smaller than belaran@999: the uncompressed version. belaran@999: belaran@999: This means that Mercurial does the right belaran@999: thing when storing a file whose native form is belaran@999: compressed, such as a zip archive or a JPEG belaran@999: image. When these types of files are compressed a second belaran@999: time, the resulting file is usually bigger than the belaran@999: once-compressed form, and so Mercurial will store the plain belaran@999: zip or JPEG. belaran@999: belaran@999: Deltas between revisions of a compressed file are usually belaran@999: larger than snapshots of the file, and Mercurial again does belaran@999: the right thing in these cases. It finds that belaran@999: such a delta exceeds the threshold at which it should store a belaran@999: complete snapshot of the file, so it stores the snapshot, belaran@999: again saving space compared to a naive delta-only belaran@999: approach. belaran@999: belaran@999: belaran@999: Network recompression belaran@999: belaran@999: When storing revisions on disk, Mercurial uses the belaran@999: deflate compression algorithm (the same one belaran@999: used by the popular zip archive format), belaran@999: which balances good speed with a respectable compression belaran@999: ratio. However, when transmitting revision data over a belaran@999: network connection, Mercurial uncompresses the compressed belaran@999: revision data. belaran@999: belaran@999: If the connection is over HTTP, Mercurial recompresses belaran@999: the entire stream of data using a compression algorithm that belaran@999: gives a better compression ratio (the Burrows-Wheeler belaran@999: algorithm from the widely used bzip2 belaran@999: compression package). This combination of algorithm and belaran@999: compression of the entire stream (instead of a revision at a belaran@999: time) substantially reduces the number of bytes to be belaran@999: transferred, yielding better network performance over most belaran@999: kinds of network. belaran@999: belaran@999: If the connection is over belaran@999: ssh, Mercurial belaran@999: doesn't recompress the stream, because belaran@999: ssh can already do this itself. You can belaran@999: tell Mercurial to always use ssh's belaran@999: compression feature by editing the belaran@999: .hgrc file in your home directory as belaran@999: follows. belaran@999: belaran@999: [ui] belaran@999: ssh = ssh -C belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Read/write ordering and atomicity belaran@999: belaran@999: Appending to files isn't the whole story when belaran@999: it comes to guaranteeing that a reader won't see a partial belaran@999: write. If you recall , belaran@999: revisions in the changelog point to revisions in the manifest, belaran@999: and revisions in the manifest point to revisions in filelogs. belaran@999: This hierarchy is deliberate. belaran@999: belaran@999: A writer starts a transaction by writing filelog and belaran@999: manifest data, and doesn't write any changelog data until belaran@999: those are finished. A reader starts by reading changelog belaran@999: data, then manifest data, followed by filelog data. belaran@999: belaran@999: Since the writer has always finished writing filelog and belaran@999: manifest data before it writes to the changelog, a reader will belaran@999: never read a pointer to a partially written manifest revision belaran@999: from the changelog, and it will never read a pointer to a belaran@999: partially written filelog revision from the manifest. belaran@999: belaran@999: belaran@999: belaran@999: Concurrent access belaran@999: belaran@999: The read/write ordering and atomicity guarantees mean that belaran@999: Mercurial never needs to lock a belaran@999: repository when it's reading data, even if the repository is belaran@999: being written to while the read is occurring. This has a big belaran@999: effect on scalability; you can have an arbitrary number of belaran@999: Mercurial processes safely reading data from a repository belaran@999: all at once, no matter whether it's being written to or belaran@999: not. belaran@999: belaran@999: The lockless nature of reading means that if you're belaran@999: sharing a repository on a multi-user system, you don't need to belaran@999: grant other local users permission to belaran@999: write to your repository in order for belaran@999: them to be able to clone it or pull changes from it; they only belaran@999: need read permission. (This is belaran@999: not a common feature among revision belaran@999: control systems, so don't take it for granted! Most require belaran@999: readers to be able to lock a repository to access it safely, belaran@999: and this requires write permission on at least one directory, belaran@999: which of course makes for all kinds of nasty and annoying belaran@999: security and administrative problems.) belaran@999: belaran@999: Mercurial uses locks to ensure that only one process can belaran@999: write to a repository at a time (the locking mechanism is safe belaran@999: even over filesystems that are notoriously hostile to locking, belaran@999: such as NFS). If a repository is locked, a writer will wait belaran@999: for a while to retry if the repository becomes unlocked, but belaran@999: if the repository remains locked for too long, the process belaran@999: attempting to write will time out after a while. This means belaran@999: that your daily automated scripts won't get stuck forever and belaran@999: pile up if a system crashes unnoticed, for example. (Yes, the belaran@999: timeout is configurable, from zero to infinity.) belaran@999: belaran@999: belaran@999: Safe dirstate access belaran@999: belaran@999: As with revision data, Mercurial doesn't take a lock to belaran@999: read the dirstate file; it does acquire a lock to write it. belaran@999: To avoid the possibility of reading a partially written copy belaran@999: of the dirstate file, Mercurial writes to a file with a belaran@999: unique name in the same directory as the dirstate file, then belaran@999: renames the temporary file atomically to belaran@999: dirstate. The file named belaran@999: dirstate is thus guaranteed to be belaran@999: complete, not partially written. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Avoiding seeks belaran@999: belaran@999: Critical to Mercurial's performance is the avoidance of belaran@999: seeks of the disk head, since any seek is far more expensive belaran@999: than even a comparatively large read operation. belaran@999: belaran@999: This is why, for example, the dirstate is stored in a belaran@999: single file. If there were a dirstate file per directory that belaran@999: Mercurial tracked, the disk would seek once per directory. belaran@999: Instead, Mercurial reads the entire single dirstate file in belaran@999: one step. belaran@999: belaran@999: Mercurial also uses a copy on write scheme belaran@999: when cloning a repository on local storage. Instead of belaran@999: copying every revlog file from the old repository into the new belaran@999: repository, it makes a hard link, which is a belaran@999: shorthand way to say these two names point to the same belaran@999: file. When Mercurial is about to write to one of a belaran@999: revlog's files, it checks to see if the number of names belaran@999: pointing at the file is greater than one. If it is, more than belaran@999: one repository is using the file, so Mercurial makes a new belaran@999: copy of the file that is private to this repository. belaran@999: belaran@999: A few revision control developers have pointed out that belaran@999: this idea of making a complete private copy of a file is not belaran@999: very efficient in its use of storage. While this is true, belaran@999: storage is cheap, and this method gives the highest belaran@999: performance while deferring most book-keeping to the operating belaran@999: system. An alternative scheme would most likely reduce belaran@999: performance and increase the complexity of the software, but belaran@999: speed and simplicity are key to the feel of belaran@999: day-to-day use. belaran@999: belaran@999: belaran@999: belaran@999: Other contents of the dirstate belaran@999: belaran@999: Because Mercurial doesn't force you to tell it when you're belaran@999: modifying a file, it uses the dirstate to store some extra belaran@999: information so it can determine efficiently whether you have belaran@999: modified a file. For each file in the working directory, it belaran@999: stores the time that it last modified the file itself, and the belaran@999: size of the file at that time. belaran@999: belaran@999: When you explicitly hg belaran@999: add, hg remove, belaran@999: hg rename or hg copy files, Mercurial updates the belaran@999: dirstate so that it knows what to do with those files when you belaran@999: commit. belaran@999: belaran@999: The dirstate helps Mercurial to efficiently belaran@999: check the status of files in a repository. belaran@999: belaran@999: belaran@999: belaran@999: When Mercurial checks the state of a file in the belaran@999: working directory, it first checks a file's modification belaran@999: time against the time in the dirstate that records when belaran@999: Mercurial last wrote the file. If the last modified time belaran@999: is the same as the time when Mercurial wrote the file, the belaran@999: file must not have been modified, so Mercurial does not belaran@999: need to check any further. belaran@999: belaran@999: belaran@999: If the file's size has changed, the file must have belaran@999: been modified. If the modification time has changed, but belaran@999: the size has not, only then does Mercurial need to belaran@999: actually read the contents of the file to see if it has belaran@999: changed. belaran@999: belaran@999: belaran@999: belaran@999: Storing the modification time and size dramatically belaran@999: reduces the number of read operations that Mercurial needs to belaran@999: perform when we run commands like hg status. belaran@999: This results in large performance improvements. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial pour une utilisation de tous les jours belaran@999: belaran@999: belaran@999: Informer Mercurial des fichier à suivre belaran@999: belaran@999: Mercurial ne suit pas les fichiers de votre dépôt tant belaran@999: que vous ne lui avez pas dit de les gérer. La commande hg status vous dira quels fichiers sont belaran@999: inconnus de Mercurial. Il utilise un belaran@999: ? pour montrer ces fichiers. belaran@999: belaran@999: Pour informer Mercurial de suivre un fichier, utilisez belaran@999: la commande hg add. Une fois que vous belaran@999: avez ajouté un fichier, la ligne correspondante à ce fichier dans la belaran@999: sortie de hg status change de belaran@999: ? à belaran@999: A. belaran@999: belaran@999: belaran@999: $ hg init add-example belaran@999: $ cd add-example belaran@999: $ echo a > myfile.txt belaran@999: $ hg status belaran@999: ? myfile.txt belaran@999: $ hg add myfile.txt belaran@999: $ hg status belaran@999: A myfile.txt belaran@999: $ hg commit -m 'Added one file' belaran@999: $ hg status belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Après avoir exécuté un hg belaran@999: commit, les fichiers que vous avez ajoutés avant le commit belaran@999: ne seront plus listés dans la sortie de hg belaran@999: status. La raison de ceci est que, par défaut, hg status ne vous montre que les fichiers belaran@999: intéressants —ceux que vous avez (par exemple) belaran@999: modifiés, supprimés ou renommés. Si vous aviez un dépôt qui contient un belaran@999: millier de fichiers, vous ne voudriez certainement que rarement entendre belaran@999: parler des fichiers que Mercurial suit, mais qui n'ont pas changés. belaran@999: (Vous pouvez quand même avoir cette information, nous y reviendrons belaran@999: plus tard.) belaran@999: belaran@999: Une fois que vous ajoutez un fichier, Mercurial ne fait belaran@999: rien du tout avec celui-ci immédiatement. Au lieu de ça, il va prendre belaran@999: un "snapshot" de l'état du fichier la prochaine fois que vous belaran@999: exécuterez un commit. Il continuera ensuite à suivre les changements belaran@999: que vous avez fait au fichier chaque fois que vous committerez, et ce, belaran@999: jusqu'à ce que vous supprimiez le fichier. belaran@999: belaran@999: belaran@999: Nommage des fichiers explicite versus implicite belaran@999: belaran@999: Un comportement utile que Mercurial possède est que si belaran@999: vous passez le nom d'un répertoire à une commande, toute commande belaran@999: Mercurial la traitera comme : Je veux opérer sur chaque fichier belaran@999: dans ce répertoire et ses sous-répertoires. belaran@999: belaran@999: belaran@999: $ mkdir b belaran@999: $ echo b > b/somefile.txt belaran@999: $ echo c > b/source.cpp belaran@999: $ mkdir b/d belaran@999: $ echo d > b/d/test.h belaran@999: $ hg add b belaran@999: adding b/d/test.h belaran@999: adding b/somefile.txt belaran@999: adding b/source.cpp belaran@999: $ hg commit -m 'Added all files in subdirectory' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Remarquez que dans cet exemple, Mercurial affiche le belaran@999: nom des fichiers qu'il a ajouté, alors qu'il ne l'a pas fait lorsque belaran@999: nous avons ajouté le fichier nommé myfile.txt belaran@999: dans l'exemple précédent. belaran@999: belaran@999: Ce qu'il se passe est que dans le premier cas, nous belaran@999: avons nommé explicitement le fichier à ajouter sur la ligne de belaran@999: commande. Ce que Mercurial suppose dans ce cas est que nous savons ce belaran@999: que nous faisons, il n'affiche donc rien en sortie. belaran@999: belaran@999: Cependant, lorsque nous avons belaran@999: implicitement donné les fichiers à l'aide du nom belaran@999: d'un répertoire, Mercurial prend l'initiative d'afficher le nom de belaran@999: chaque fichier avec lequel il fait quelque chose. Ceci clarifie ce belaran@999: qu'il se passe et réduit la probabilité d'une mauvaise surprise belaran@999: restée silencieuse. Ce comportement est commun à la plupart des belaran@999: commandes Mercurial. belaran@999: belaran@999: belaran@999: Mercurial suit les fichiers, pas les répertoires belaran@999: belaran@999: Mercurial ne suit pas les informations sur les belaran@999: répertoires. En contrepartie, il suit le chemin vers un fichier. Avant belaran@999: de créer un fichier, il crée au préalable les répertoires manquants belaran@999: dans le chemin. Après avoir supprimé un fichier, il supprime chaque belaran@999: répertoire vide qui apparaît dans le chemin du fichier. Ceci apparaît belaran@999: comme une distinction triviale, cependant, cela a une conséquence belaran@999: pratique mineure : il n'est pas possible de représenter un répertoire belaran@999: totalement vide dans Mercurial. belaran@999: belaran@999: Les répertoires vides sont rarement utiles. Il existe belaran@999: cependant des solutions alternatives et non intrusives que vous belaran@999: pouvez utiliser pour obtenir l'effet approprié. Les développeurs de belaran@999: Mercurial ont ainsi pensé que la complexité requise pour gérer les belaran@999: répertoires n'était pas aussi importante que le bénéfice que cette belaran@999: fonctionnalité apporterait. belaran@999: belaran@999: Si vous avez besoin d'un répertoire vide dans votre belaran@999: dépôt, il existe quelques façons d'y arriver. L'une d'elles est de belaran@999: créer un répertoire et ensuite, de faire un hg belaran@999: add sur un fichier caché dans ce belaran@999: répertoire. Sur les systèmes de type Unix, tout fichier dont le nom belaran@999: commence avec un point (.) est belaran@999: considéré comme caché par la plupart des commandes et outils belaran@999: graphiques. Cette approche est illustrée ci-après. belaran@999: belaran@999: belaran@999: $ hg init hidden-example belaran@999: $ cd hidden-example belaran@999: $ mkdir empty belaran@999: $ touch empty/.hidden belaran@999: $ hg add empty/.hidden belaran@999: $ hg commit -m 'Manage an empty-looking directory' belaran@999: $ ls empty belaran@999: $ cd .. belaran@999: $ hg clone hidden-example tmp belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ ls tmp belaran@999: empty belaran@999: $ ls tmp/empty belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Une autre façon de s'attaquer au besoin d'un belaran@999: répertoire vide est de simplement d'en créer un dans vos scripts belaran@999: de construction avant qu'ils n'en aient le besoin. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Comment arrêter de suivre un fichier belaran@999: belaran@999: Une fois que vous décidez qu'un fichier n'appartient belaran@999: plus à votre dépôt, utilisez la commande hg belaran@999: remove. Ceci supprime le fichier et informe Mercurial belaran@999: d'arrêter de le suivre (ce qui prendra effet lors du prochain commit). belaran@999: Un fichier supprimé est représenté dans la sortie de la commande belaran@999: hg status par un belaran@999: R. belaran@999: belaran@999: belaran@999: $ hg init remove-example belaran@999: $ cd remove-example belaran@999: $ echo a > a belaran@999: $ mkdir b belaran@999: $ echo b > b/b belaran@999: $ hg add a b belaran@999: adding b/b belaran@999: $ hg commit -m 'Small example for file removal' belaran@999: $ hg remove a belaran@999: $ hg status belaran@999: R a belaran@999: $ hg remove b belaran@999: removing b/b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Après avoir fait un hg belaran@999: remove sur un fichier, Mercurial ne suivra plus aucun belaran@999: changement sur ce fichier, même si vous recréez un fichier avec le même belaran@999: nom dans votre répertoire de travail. Si vous recréez un fichier avec le belaran@999: même nom et que vous désirez que Mercurial suive ce dernier, faite belaran@999: simplement un hg add sur celui-ci. belaran@999: Mercurial saura alors que le nouveau fichier ne fait pas référence à belaran@999: l'ancien fichier qui portait le même nom. belaran@999: belaran@999: belaran@999: Supprimer un fichier n'affecte pas son historique belaran@999: belaran@999: Il est important de comprendre que supprimer un fichier belaran@999: n'a que deux effets. belaran@999: belaran@999: belaran@999: Il supprime la version actuelle de ce belaran@999: fichier du répertoire de travail. belaran@999: belaran@999: Il arrête, à partir du prochain commit, le belaran@999: suivi de Mercurial sur les changements qui ont lieu sur ce belaran@999: fichier. belaran@999: belaran@999: belaran@999: Supprimer un fichier n'affecte en belaran@999: aucun cas l'historique du belaran@999: fichier. belaran@999: belaran@999: Si vous mettez à jour le répertoire de travail à un belaran@999: changeset qui a été committé alors que le fichier que vous venez de belaran@999: supprimer était encore suivi, ce fichier réapparaîtra dans le belaran@999: répertoire de travail, avec le contenu qu'il avait lorsque vous aviez belaran@999: committé ce changeset. Si vous mettez à jour (update) le répertoire de belaran@999: travail à un changeset ultérieur dans lequel le fichier a été belaran@999: supprimé, Mercurial supprimera une nouvelle fois le fichier du belaran@999: répertoire de travail. belaran@999: belaran@999: belaran@999: belaran@999: Fichiers manquants belaran@999: belaran@999: Mercurial considère qu'un fichier que vous avez belaran@999: supprimé sans utiliserhg remove belaran@999: comme étant manquant. Un fichier manquant est belaran@999: représenté avec un ! en sortie de belaran@999: hg status. belaran@999: Les commandes Mercurial ne feront rien avec les fichiers belaran@999: manquants. belaran@999: belaran@999: belaran@999: $ hg init missing-example belaran@999: $ cd missing-example belaran@999: $ echo a > a belaran@999: $ hg add a belaran@999: $ hg commit -m 'File about to be missing' belaran@999: $ rm a belaran@999: $ hg status belaran@999: ! a belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si votre dépôt contient un fichier que hg status reporte comme manquant, et que belaran@999: vous voulez que ce fichier reste supprimé, vous pouvez exécuter belaran@999: hg remove à tout moment belaran@999: pour dire à Mercurial que vous aviez bien voulu supprimer ce belaran@999: fichier. belaran@999: belaran@999: belaran@999: $ hg remove --after a belaran@999: $ hg status belaran@999: R a belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: D'un autre coté, si vous avez supprimé le fichier belaran@999: manquant par accident, donnez à la commande hg belaran@999: revert le nom du fichier à retrouver. Il réapparaitra dans belaran@999: sa forme non modifiée. belaran@999: belaran@999: belaran@999: $ hg revert a belaran@999: $ cat a belaran@999: a belaran@999: $ hg status belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Entre nous : Pourquoi dire explicitement à Mercurial de supprimer un belaran@999: fichier ? belaran@999: belaran@999: Vous pourriez vous demander pourquoi il est nécessaire belaran@999: de dire explicitement à Mercurial que vous souhaitez supprimer un belaran@999: fichier. Au début du développement de Mercurial, celui ci vous belaran@999: laissait pourtant supprimer un fichier sans soucis ; Mercurial vous belaran@999: aurait automatiquement informé de l'absence du fichier lorsque vous belaran@999: auriez lancé un hg commit et arrêté belaran@999: de le suivre. En pratique, ceci a montré qu'il était trop facile de belaran@999: supprimer accidentellement un fichier sans le remarquer. belaran@999: belaran@999: belaran@999: belaran@999: Raccourci utile—ajouter et supprimer des fichiers en une belaran@999: seule étape. belaran@999: belaran@999: Mercurial offre une commande combinée, hg addremove, qui ajoute les fichiers non belaran@999: suivis et marque les fichiers manquants comme supprimés. belaran@999: belaran@999: belaran@999: $ hg init addremove-example belaran@999: $ cd addremove-example belaran@999: $ echo a > a belaran@999: $ echo b > b belaran@999: $ hg addremove belaran@999: adding a belaran@999: adding b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La commande hg commit belaran@999: fournit aussi une option qui belaran@999: exécute le même ajouter-et-supprimer, immédiatement suivi d'un belaran@999: commit. belaran@999: belaran@999: belaran@999: $ echo c > c belaran@999: $ hg commit -A -m 'Commit with addremove' belaran@999: adding c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Copier des fichiers belaran@999: belaran@999: Mercurial fournit une commande hg belaran@999: copy qui vous permet de faire une nouvelle copie d'un belaran@999: fichier. Lorsque vous copiez un fichier en utilisant cette commande, belaran@999: Mercurial crée un enregistrement du fait que ce nouveau fichier est une belaran@999: copie du fichier originel. Il traite ces fichiers copiés spécialement belaran@999: lorsque vous fusionnez (merge) votre travail avec quelqu'un belaran@999: d'autre. belaran@999: belaran@999: belaran@999: Les résultats d'une copie durant une fusion (merge) belaran@999: belaran@999: Ce qu'il se passe durant une fusion (merge) est que belaran@999: les changements suivent une copie. Pour illustrer ce belaran@999: que cela veut dire de la meilleure façon, créons un exemple. Nous belaran@999: allons commencer avec le mini dépôt usuel qui contient un simple belaran@999: fichier. belaran@999: belaran@999: belaran@999: $ hg init my-copy belaran@999: $ cd my-copy belaran@999: $ echo line > file belaran@999: $ hg add file belaran@999: $ hg commit -m 'Added a file' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous devons faire du travail en parallèle, ainsi, belaran@999: nous aurons quelque chose à fusionner (merge). Donc clonons notre belaran@999: dépôt. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone my-copy your-copy belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: De retour dans notre dépôt initial, utilisons la belaran@999: commande hg copy pour faire une belaran@999: copie du premier fichier que nous avons créé. belaran@999: belaran@999: belaran@999: $ cd my-copy belaran@999: $ hg copy file new-file belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si nous regardons ensuite à la sortie de la commande belaran@999: hg status, les fichiers copiés belaran@999: ont l'air de fichiers normalement ajoutés. belaran@999: belaran@999: belaran@999: $ hg status belaran@999: A new-file belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mais si nous passons l'option à hg belaran@999: status, il affiche une autre ligne de sortie : il s'agit belaran@999: du fichier source pour notre copie. belaran@999: belaran@999: belaran@999: $ hg status -C belaran@999: A new-file belaran@999: file belaran@999: $ hg commit -m 'Copied file' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Maintenant, de retour dans le dépôt que nous avons belaran@999: cloné, créons un changement en parallèle. Nous allons ajouter une belaran@999: ligne de contenu au fichier original qui a été créé. belaran@999: belaran@999: belaran@999: $ cd ../your-copy belaran@999: $ echo 'new contents' >> file belaran@999: $ hg commit -m 'Changed file' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous avons alors un fichier file belaran@999: modifié dans ce dépôt. Lorsque nous récupérons (pull) les changements belaran@999: depuis le premier répertoire et fusionnons (merge) les deux "heads", belaran@999: Mercurial propagera les changements que nous avons faits localement belaran@999: au fichier file dans sa copie belaran@999: new-file. belaran@999: belaran@999: belaran@999: $ hg pull ../my-copy belaran@999: pulling from ../my-copy belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files (+1 heads) belaran@999: (run 'hg heads' to see heads, 'hg merge' to merge) belaran@999: $ hg merge belaran@999: merging file and new-file to new-file belaran@999: 0 files updated, 1 files merged, 0 files removed, 0 files unresolved belaran@999: (branch merge, don't forget to commit) belaran@999: $ cat new-file belaran@999: line belaran@999: new contents belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Pourquoi est-ce que les changements devraient suivre les copies belaran@999: ? belaran@999: belaran@999: Ce comportement—des changements d'un fichiers belaran@999: qui se propagent aux copies de ce fichier—peut sembler belaran@999: ésotérique, mais, dans la plupart des cas, c'est hautement belaran@999: désirable. belaran@999: belaran@999: Pour commencer, souvenez vous que cette propagation belaran@999: a lieue seulement lors des fusions (merge). belaran@999: Donc, si vous faites un hg copy sur belaran@999: un fichier, et par la suite modifiez le fichier original durant le belaran@999: cours normal de votre travail, rien n'a lieu. belaran@999: belaran@999: La deuxième chose à savoir c'est que les modifications belaran@999: ne se propageront à travers une copie que si le changeset à partir belaran@999: duquel vous faites une fusion (merge) n'a pas encore belaran@999: vu la copie. belaran@999: belaran@999: La raison pour laquelle Mercurial fait ainsi est une belaran@999: règle. Imaginons que je corrige un important bug dans un fichier source belaran@999: et que je commit mes changements. Pendant ce temps, vous avez décidé de belaran@999: faire un hg copy du fichier dans belaran@999: votre dépôt, sans rien savoir au sujet du bug ou à propos de la belaran@999: correction. Vous avez alors commencé à "hacker" sur votre copie du belaran@999: fichier. belaran@999: belaran@999: Si vous aviez récupéré (pull) et fusionné (merge) mes belaran@999: changements, et que Mercurial n'avait pas belaran@999: propagé les changements à travers les copies, votre nouveau fichier belaran@999: source contiendrait maintenant le bug, et à moins que vous ne sachiez belaran@999: qu'il faille propager la correction du bug à la main, le bug aurait belaran@999: subsisté dans votre copie du fichier. belaran@999: belaran@999: En propageant automatiquement les changements qui belaran@999: fixent les bugs à partir du fichier original vers les copies, belaran@999: Mercurial prévient ce type de problèmes. A ma connaissance, Mercurial belaran@999: est le seul système de gestion de révisions qui belaran@999: propage les changements à travers les copies comme ceci. belaran@999: belaran@999: Une fois que votre historique des changements a un belaran@999: enregistrement concernant une copie et qu'une fusion postérieure a belaran@999: eu lieue, il n'y a d'habitude pas d'autre besoin de propager les belaran@999: changements du fichier originel vers le fichier copié. C'est pourquoi belaran@999: Mercurial ne propage les changements à travers les copies qu'à la belaran@999: première fusion, et pas d'avantage. belaran@999: belaran@999: belaran@999: belaran@999: Comment faire des changements qui <emphasis>ne</emphasis> belaran@999: suivent <emphasis>pas</emphasis> une copie belaran@999: belaran@999: Si pour une raison ou une autre, vous décidez que belaran@999: cette fonctionnalité de propager automatiquement les changements à belaran@999: travers les copies n'est pas pour vous, utilisez simplement la belaran@999: commande normale de copie de votre système (sur les systèmes de type belaran@999: Unix, il s'agit de cp) pour faire une copie d'un belaran@999: fichier. Utilisez ensuite hg add belaran@999: pour ajouter les nouveaux fichiers à la main. Cependant, avant d'en belaran@999: faire ainsi, relisez , et faites belaran@999: un choix en connaissance de cause comme quoi cette fonctionnalité belaran@999: n'est pas appropriée à votre cas spécifique. belaran@999: belaran@999: belaran@999: belaran@999: Comportement de la commande <command role="hg-cmd" moreinfo="none">hg copy</command> belaran@999: belaran@999: Lorsque vous utilisez la commande hg copy, Mercurial crée une copie de chaque belaran@999: fichier source tel qu'il est actuellement dans le répertoire de belaran@999: travail. Cela signifie que si vous effectuez des modifications sur un belaran@999: fichier, puis faites un hg copy sur belaran@999: celui-ci sans avoir au préalable committé ces changements, la nouvelle belaran@999: copie contiendra aussi les modifications que vous avez fait jusqu'à belaran@999: ce point. (Je trouve ce comportement quelque peu contre intuitif, belaran@999: c'est pourquoi j'en fais mention ici.) belaran@999: belaran@999: belaran@999: La commande hg copy belaran@999: agit comme la commande Unix cp (vous pouvez belaran@999: utilisez l'alias hg cp si vous belaran@999: préférez). Nous devons lui donner deux ou plus arguments où le belaran@999: dernier est considéré comme la destination, et belaran@999: les autres comme les sources. belaran@999: belaran@999: Si vous passez à hg belaran@999: copy un seul fichier source, et que la destination belaran@999: n'existe pas, ceci créera un nouveau fichier avec ce nom. belaran@999: belaran@999: belaran@999: $ mkdir k belaran@999: $ hg copy a k belaran@999: $ ls k belaran@999: a belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si la destination est un répertoire, Mercurial copie belaran@999: les sources dans ce répertoire. belaran@999: belaran@999: belaran@999: $ mkdir d belaran@999: $ hg copy a b d belaran@999: $ ls d belaran@999: a b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La copie de répertoire est récursive et préserve la belaran@999: structure du répertoire source. belaran@999: belaran@999: belaran@999: $ hg copy z e belaran@999: copying z/a/c to e/a/c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Si la source et la destination sont tous deux des belaran@999: répertoires, l'arborescence de la source est recréée dans le belaran@999: répertoire destination. belaran@999: belaran@999: belaran@999: $ hg copy z d belaran@999: copying z/a/c to d/z/a/c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Comme avec la commande hg belaran@999: remove, si vous copiez un fichier manuellement et voulez belaran@999: que Mercurial sache qu'il s'agit d'une copie, utilisez simplement belaran@999: l'option avec hg copy. belaran@999: belaran@999: belaran@999: $ cp a n belaran@999: $ hg copy --after a n belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Renommer les fichiers belaran@999: belaran@999: Il est plus commun d'avoir besoin de renommer un belaran@999: fichier que d'en faire une copie. La raison pour laquelle j'ai discuté belaran@999: de la commande hg copy avant de parler belaran@999: de renommage des fichiers est que Mercurial traite les renommages belaran@999: essentiellement comme une copie. Ainsi, savoir comment Mercurial traite belaran@999: les copies de fichiers vous informe sur ce que vous êtes en droit belaran@999: d'attendre lorsque vous renommez un fichier. belaran@999: belaran@999: Lorsque vous utilisez la commande hg rename, Mercurial crée une copie de tous belaran@999: les fichiers sources, les supprime et marque ces fichiers comme étant belaran@999: supprimés. belaran@999: belaran@999: belaran@999: $ hg rename a b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La commande hg status belaran@999: montre les nouveaux fichiers comme ajoutés et les fichiers originaux belaran@999: comme supprimés. belaran@999: belaran@999: belaran@999: $ hg status belaran@999: A b belaran@999: R a belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: A cause du hg copy, belaran@999: nous devons utiliser l'option belaran@999: pour la commande hg status afin belaran@999: d'observer que le fichier ajouté est bien suivi par Mercurial comme belaran@999: étant une copie de l'original maintenant supprimé. belaran@999: belaran@999: belaran@999: $ hg status -C belaran@999: A b belaran@999: a belaran@999: R a belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Comme avec hg remove et belaran@999: hg copy, vous pouvez informer belaran@999: Mercurial au sujet d'un renommage après coup en utilisant l'option belaran@999: . Dans le plus grand belaran@999: respect, le comportement de la commande hg belaran@999: rename, et les options qu'il accepte sont similaires à la belaran@999: commande hg copy. belaran@999: belaran@999: Si vous êtes familier avec la ligne de commande Unix, belaran@999: vous serez heureux d'apprendre que la commande hg rename peut être invoquée par hg mv. belaran@999: belaran@999: belaran@999: Renommer les fichiers et fusionner (merge) les changements belaran@999: belaran@999: Puise que le "rename" de Mercurial est implanté comme un belaran@999: "copy-and-remove", la même propagation des changements a lieue après belaran@999: un "rename" qu'après un "copy" lorsque vous fusionnez (merge). belaran@999: belaran@999: Si je modifie un fichier et que vous le renommez, si belaran@999: ensuite nous fusionnons nos changements respectifs, mes modifications belaran@999: sur le fichier sous son nom originel seront propagés vers le même belaran@999: fichier sous son nouveau nom. (C'est quelque chose que vous pourriez belaran@999: espérer voir fonctionner simplement, mais tous les belaran@999: systèmes de gestion de version ne le font pas.) belaran@999: belaran@999: Tandis qu'avoir des changements qui suivent une copie belaran@999: est une fonctionnalité où vous hocheriez sûrement la tête en disant belaran@999: oui, cela pourrait être utile, il est clair que les belaran@999: voir suivre un renommage est définitivement important. Sans cette belaran@999: aptitude, il serait vraiment trop facile d'avoir des changements belaran@999: qui deviennent orphelins lorsque des fichiers sont renommés. belaran@999: belaran@999: belaran@999: belaran@999: Renommages divergeants et fusion (merge) belaran@999: belaran@999: Le cas de noms divergeants a lieu lorsque deux belaran@999: développeurs commencent avec un fichier—appelons le belaran@999: foo—dans leurs dépôts respectifs. belaran@999: belaran@999: belaran@999: $ hg clone orig anne belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ hg clone orig bob belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Anne renomme le fichier en belaran@999: bar. belaran@999: belaran@999: belaran@999: $ cd anne belaran@999: $ hg rename foo bar belaran@999: $ hg ci -m 'Rename foo to bar' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Pendant ce temps, Bob le renomme en belaran@999: quux. (Souvenez vous que hg mv est un alias pour hg rename.) belaran@999: belaran@999: belaran@999: $ cd ../bob belaran@999: $ hg mv foo quux belaran@999: $ hg ci -m 'Rename foo to quux' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: J'aime à penser qu'il s'agit d'un conflit puisque belaran@999: chaque développeur a exprimé différentes intentions au sujet de ce belaran@999: que le nom de ce fichier aurait du être. belaran@999: belaran@999: Que pensez vous qu'il devrait se produire lorsqu'ils belaran@999: fusionnent (merge) leurs travaux ? Le comportement actuel de belaran@999: Mercurial est qu'il préserve toujours les deux belaran@999: noms lorsqu'il fusionne (merge) des changesets qui contiennent des belaran@999: renommages divergeants. belaran@999: belaran@999: belaran@999: # See http://www.selenic.com/mercurial/bts/issue455 belaran@999: $ cd ../orig belaran@999: $ hg pull -u ../anne belaran@999: pulling from ../anne belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: 1 files updated, 0 files merged, 1 files removed, 0 files unresolved belaran@999: $ hg pull ../bob belaran@999: pulling from ../bob belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files (+1 heads) belaran@999: (run 'hg heads' to see heads, 'hg merge' to merge) belaran@999: $ hg merge belaran@999: warning: detected divergent renames of foo to: belaran@999: bar belaran@999: quux belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: (branch merge, don't forget to commit) belaran@999: $ ls belaran@999: bar quux belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Remarquez que bien que Mercurial vous avertisse au belaran@999: sujet de la divergeance des renommages, il vous laisse faire quelque belaran@999: chose au sujet de la divergeance après la fusion (merge). belaran@999: belaran@999: belaran@999: belaran@999: Renommages et fusion convergeants belaran@999: belaran@999: Un autre type de conflit de renommage intervient belaran@999: lorsque deux personne choisissent de renommer différents fichiers belaran@999: source vers la même belaran@999: destination. Dans ce cas, Mercurial exécute la belaran@999: machinerie normale de fusion (merge) et vous guide vers une belaran@999: solution convenable. belaran@999: belaran@999: belaran@999: belaran@999: Autres cas anguleux relatifs aux noms belaran@999: belaran@999: Mercurial possède un bug de longue date dans lequel il belaran@999: échoue à traiter une fusion (merge) où un coté a un fichier avec un belaran@999: nom donné, alors que l'autre coté possède un répertoire avec le même nom. belaran@999: Ceci est documenté dans l'issue belaran@999: 29. belaran@999: belaran@999: belaran@999: $ hg init issue29 belaran@999: $ cd issue29 belaran@999: $ echo a > a belaran@999: $ hg ci -Ama belaran@999: adding a belaran@999: $ echo b > b belaran@999: $ hg ci -Amb belaran@999: adding b belaran@999: $ hg up 0 belaran@999: 0 files updated, 0 files merged, 1 files removed, 0 files unresolved belaran@999: $ mkdir b belaran@999: $ echo b > b/b belaran@999: $ hg ci -Amc belaran@999: adding b/b belaran@999: created new head belaran@999: $ hg merge belaran@999: abort: Is a directory: /tmp/issue29vhrzWD/issue29/b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Récupération d'erreurs belaran@999: belaran@999: Mercurial possède certaines commandes utiles qui vont belaran@999: vous aider à récupérer de certaines erreurs communes. belaran@999: belaran@999: La commande hg revert belaran@999: vous permet d'annuler les changements que vous avez faits dans votre belaran@999: répertoire de travail. Par exemple, si vous faites un hg add sur un fichier par accident, exécutez belaran@999: juste hg revert avec le nom du fichier belaran@999: que vous avez ajouté et tandis que le fichier ne sera touché d'une belaran@999: quelconque manière, il ne sera plus suivi comme ajouté par Mercurial. belaran@999: Vous pouvez aussi utiliser la commande hg belaran@999: revert pour vous débarrasser de modifications erronés belaran@999: apportées à un fichier. belaran@999: belaran@999: Il est utile de se souvenir que la commande hg revert est utile pour les modifications belaran@999: qui n'ont pas encore été committées. Une fois que vous avez committé un belaran@999: changement, si vous décidez qu'il s'agissait d'une erreur, vous pouvez belaran@999: toujours faire quelque chose à ce sujet, bien que vos options soient belaran@999: un peu plus limitées. belaran@999: belaran@999: Pour plus d'informations au sujet de la commande belaran@999: hg revert, et des détails sur comment belaran@999: traiter les modifications que vous avez déjà committées, référez vous à belaran@999: . belaran@999: belaran@999: belaran@999: belaran@999: Traiter avec les fusions (merge) malicieuses belaran@999: belaran@999: Dans des projets compliqués ou conséquents, il n'est pas belaran@999: rare qu'une fusion (merge) de deux changesets finisse par une migraine. belaran@999: Supposez qu'il y ait un gros fichier source qui ait été largement édité de belaran@999: chaque coté de la fusion (merge) : ceci va inévitablement résulter en belaran@999: conflits, dont certains peuvent prendre plusieurs essais pour s'en belaran@999: sortir. belaran@999: belaran@999: Développons en un cas simple pour voir comment le gérer. belaran@999: Nous allons commencer avec un dépôt contenant un fichier, et le belaran@999: cloner deux fois. belaran@999: belaran@999: belaran@999: $ hg init conflict belaran@999: $ cd conflict belaran@999: $ echo first > myfile.txt belaran@999: $ hg ci -A -m first belaran@999: adding myfile.txt belaran@999: $ cd .. belaran@999: $ hg clone conflict left belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ hg clone conflict right belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Dans un des clones, nous allons modifier le fichier belaran@999: d'une façon. belaran@999: belaran@999: belaran@999: $ cd left belaran@999: $ echo left >> myfile.txt belaran@999: $ hg ci -m left belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Dans un autre, nous allons modifier le fichier belaran@999: différemment. belaran@999: belaran@999: belaran@999: $ cd ../right belaran@999: $ echo right >> myfile.txt belaran@999: $ hg ci -m right belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Ensuite, nous allons récupérer (pull) chaque ensemble de belaran@999: changement dans notre dépôt original. belaran@999: belaran@999: belaran@999: $ cd ../conflict belaran@999: $ hg pull -u ../left belaran@999: pulling from ../left belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ hg pull -u ../right belaran@999: pulling from ../right belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files (+1 heads) belaran@999: not updating, since new heads added belaran@999: (run 'hg heads' to see heads, 'hg merge' to merge) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous nous attendons à ce que notre dépôt contienne deux belaran@999: "heads". belaran@999: belaran@999: belaran@999: $ hg heads belaran@999: changeset: 2:85f1afc84c33 belaran@999: tag: tip belaran@999: parent: 0:14a820f81f48 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:51 2009 +0000 belaran@999: summary: right belaran@999: belaran@999: changeset: 1:085ebbf44348 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:51 2009 +0000 belaran@999: summary: left belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Normalement, si nous lançons hg belaran@999: merge à ce point, il nous renverra vers une interface belaran@999: utilisateur qui nous permettra de résoudre manuellement les éditions belaran@999: conflictuelles sur le fichier myfile.txt. belaran@999: Cependant, pour simplifier ici les choses dans la présentation, nous belaran@999: aimerions plutôt que la fusion (merge) échoue immédiatement. Voici une belaran@999: façon de le faire. belaran@999: belaran@999: belaran@999: $ export HGMERGE=false belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nous avons dit au processus de fusion de Mercurial belaran@999: d'exécuter la commande false (qui échoue belaran@999: immédiatement, à la demande) s'il détecte une fusion (merge) qu'il ne belaran@999: peut pas arranger automatiquement. belaran@999: belaran@999: Si nous appelons maintenant hg belaran@999: merge, il devrait échouer et reporter une erreur. belaran@999: belaran@999: belaran@999: $ hg merge belaran@999: merging myfile.txt belaran@999: merging myfile.txt failed! belaran@999: 0 files updated, 0 files merged, 0 files removed, 1 files unresolved belaran@999: use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Même si nous ne remarquons pas qu'une fusion (merge) a belaran@999: échoué, Mercurial nous empêchera de committer le résultat d'une fusion belaran@999: ratée. belaran@999: belaran@999: belaran@999: $ hg commit -m 'Attempt to commit a failed merge' belaran@999: abort: unresolved merge conflicts (see hg resolve) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Lorsque hg commit belaran@999: échoue dans ce cas, il suggère que nous utilisons la commande peu belaran@999: connue hg resolve. Comme d'habitude, belaran@999: hg help resolve affichera une aide belaran@999: sommaire. belaran@999: belaran@999: belaran@999: États de résolution des fichiers belaran@999: belaran@999: belaran@999: Lorsqu'une fusion intervient, la plupart des fichiers belaran@999: vont, la plupart du temps, rester sans modification. Pour chaque belaran@999: fichier sur lequel Mercurial doit faire quelque chose, il suit l'état belaran@999: de celui-ci. belaran@999: belaran@999: belaran@999: Un fichier belaran@999: resolved a été fusionné belaran@999: (merge) avec succès, que ce soit automatiquement par Mercurial ou belaran@999: manuellement par une intervention humaine. belaran@999: Un fichier belaran@999: unresolved n'a pas été belaran@999: fusionné (merge) correctement et a besoin de plus belaran@999: d'attention. belaran@999: belaran@999: belaran@999: belaran@999: Si Mercurial voit un fichier belaran@999: quelconque dans un état belaran@999: unresolved après une fusion (merge), il considère que belaran@999: la fusion (merge) a échoué. Heureusement, nous n'avons pas à belaran@999: recommencer la procédure à partir du début. belaran@999: belaran@999: L'option belaran@999: ou passée à hg resolve liste l'état de chaque fichier belaran@999: fusionné (merge). belaran@999: belaran@999: belaran@999: $ hg resolve -l belaran@999: U myfile.txt belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: En sortie de hg belaran@999: resolve, un fichier "resolved" est marqué avec un belaran@999: R, alors qu'un fichier "unresolved" est marqué belaran@999: d'un U. S'il existe un fichier listé avec un belaran@999: U, nous savons qu'essayer de committer le résultat belaran@999: de la fusion (merge) échouera. belaran@999: belaran@999: belaran@999: belaran@999: Résoudre une fusion de fichier belaran@999: belaran@999: Nous avons plusieurs options pour changer l'état d'un belaran@999: fichier de "unresolved" à "resolved". Le plus commun est de relancer belaran@999: hg resolve. Si nous passons les noms belaran@999: des fichiers individuels ou des répertoires, ceci retentera la fusion belaran@999: de tous les fichiers présents à cet endroit. Nous pouvons aussi belaran@999: passer l'option ou belaran@999: qui tentera de fusionner belaran@999: tous les fichiers "unresolved". belaran@999: belaran@999: Mercurial nous laisse aussi modifier la résolution belaran@999: d'un fichier directement. Nous pouvons marquer un fichier "resolved" belaran@999: en utilisant l'option , belaran@999: ou "unresolved" en utilisant l'option . Ceci nous autorise à belaran@999: nettoyer une fusion particulièrement compliquée à la main, et de belaran@999: garder un suivi de nos progrès avec chaque fichier pendant que nous belaran@999: procédons. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Des "diffs" plus utiles belaran@999: belaran@999: La sortie par défaut de la commande hg diff est compatible rétrospectivement avec belaran@999: la commande régulière diff, mais ceci a quelques belaran@999: inconvénients. belaran@999: belaran@999: Considérez le cas où nous utilisons hg belaran@999: rename pour renommer un fichier. belaran@999: belaran@999: belaran@999: $ hg rename a b belaran@999: $ hg diff belaran@999: diff -r f5deb7868663 a belaran@999: --- a/a Sun Aug 16 14:04:49 2009 +0000 belaran@999: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 belaran@999: @@ -1,1 +0,0 @@ belaran@999: -a belaran@999: diff -r f5deb7868663 b belaran@999: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 belaran@999: +++ b/b Sun Aug 16 14:04:49 2009 +0000 belaran@999: @@ -0,0 +1,1 @@ belaran@999: +a belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La sortie de hg diff belaran@999: ci-dessus cache le fait que nous avons simplement renommé un fichier. belaran@999: La commande hg diff accepte l'option belaran@999: ou pour utiliser un nouveau belaran@999: format de diff qui montre ces informations sous une forme plus belaran@999: expressive. belaran@999: belaran@999: belaran@999: $ hg diff -g belaran@999: diff --git a/a b/b belaran@999: rename from a belaran@999: rename to b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Cette option peut aussi aider avec le cas autrement belaran@999: confus : un fichier qui apparaît comme étant modifié en accord avec belaran@999: hg status, mais où hg diff n'affiche rien. Cette situation peut belaran@999: survenir si nous changeons les permissions d'exécution du belaran@999: fichier. belaran@999: belaran@999: belaran@999: $ chmod +x a belaran@999: $ hg st belaran@999: M a belaran@999: $ hg diff belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: La commande normale diff ne fait pas belaran@999: attention aux permissions des fichiers, ce qui explique pourquoi belaran@999: hg diff n'affiche rien du tout par belaran@999: défaut. Si nous lui passons l'option , ceci nous belaran@999: informe de ce qu'il s'est vraiment passé. belaran@999: belaran@999: belaran@999: $ hg diff -g belaran@999: diff --git a/a b/a belaran@999: old mode 100644 belaran@999: new mode 100755 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Quels fichiers suivre et lesquels éviter belaran@999: belaran@999: Les systèmes de gestion de révisions sont en général belaran@999: meilleurs pour gérer les fichiers textes qui sont écrits par les belaran@999: humains, comme le code source, où les fichiers ne changent pas belaran@999: énormément d'une révision à l'autre. Certains systèmes de gestion de belaran@999: révisions centralisés peuvent aussi traiter très convenablement les belaran@999: fichiers binaires, tels que les images bitmap. belaran@999: belaran@999: Par exemple, une équipe de développement de jeux va belaran@999: probablement gérer les deux types : ses codes source et tous ses binaires belaran@999: (ex. données géométriques, textures, schémas de cartes) dans un système belaran@999: de contrôle de révisions. belaran@999: belaran@999: belaran@999: Puisqu'il est d'habitude impossible de fusionner (merge) belaran@999: deux modifications conflictuelles sur un fichier binaire, les systèmes belaran@999: de version centralisés offrent souvent un mécanisme de verrou (lock) qui belaran@999: permet à un utilisateur de dire Je suis la seule personne qui belaran@999: peut éditer ce fichier. belaran@999: belaran@999: En comparaison avec un système centralisé, un système belaran@999: décentralisé de gestion de révision change certains facteurs qui belaran@999: guident les décisions sur quels fichiers gérer et comment. belaran@999: belaran@999: Par exemple, un système distribué de gestion de révisions belaran@999: ne peut pas, par sa nature, offrir un système de véroux (lock) sur les belaran@999: fichiers. Il n'y a donc pas de mécanisme inclus pour empêcher deux belaran@999: personnes de faire des modifications conflictuelles sur un fichier belaran@999: binaire. Si vous avez une équipe où plusieurs personnes peuvent souvent belaran@999: éditer un fichier binaire, cela ne serait pas une très bonne idée belaran@999: d'utiliser Mercurial —ou tout autre système distribué de gestion belaran@999: de révisions—pour gérer ces fichiers. belaran@999: belaran@999: Lorsque vous sauvegardez les modifications sur un belaran@999: fichier, Mercurial ne sauvegarde d'habitude que les différences entre belaran@999: la version précédente et la version actuelle d'un fichier. Pour la belaran@999: plupart des fichiers texte, ceci est très efficace. Cependant, certains belaran@999: fichiers (en particulier les fichiers binaires) sont construits d'une belaran@999: façon que même un petit changement sur un contenu logique résulte sur belaran@999: un changement de la plupart des octets du fichier. Par exemple, les belaran@999: fichiers compressés sont particulièrement sujets à ce comportement. Si belaran@999: les différences entre deux versions successives d'un fichier sont belaran@999: toujours très grandes, Mercurial ne sera pas capable de sauvegarder belaran@999: l'historique des révisions sur le fichier très efficacement. Ceci peut belaran@999: affecter aussi bien les besoins pour la sauvegarde locale que le temps belaran@999: nécessaire à cloner le dépôt. belaran@999: belaran@999: Pour avoir une idée de comment ceci pourrait vous belaran@999: affecter en pratique, supposez que nous voulions que Mercurial gère des belaran@999: documents OpenOffice. OpenOffice sauvegarde les documents sur le disque belaran@999: comme des fichiers compressés zip. Même le fait d'éditer ces fichiers belaran@999: d'une seule lettre, changera les bits de la quasi totalité du fichier belaran@999: lorsque vous le sauvegarderez. Maintenant, supposez que ce fichier belaran@999: fasse une taille de 2Mo. Puisque la plupart du fichier change à chaque belaran@999: fois que vous sauvegardez, Mercurial aura à sauvegarder tous les 2Mo du belaran@999: fichier à chaque commit, alors que de votre point de vue, il n'y a belaran@999: que peu de mots qui changent à chaque fois. Un seul fichier belaran@999: souvent édité qui n'est pas bien traité par les hypothèses que Mercurial belaran@999: fait sur les sauvegardes peut facilement avoir un effet colossal sur la belaran@999: taille du dépôt. belaran@999: belaran@999: Même pire, si vous et quelqu'un d'autre éditez le même belaran@999: document OpenOffice sur lequel vous travaillez, il n'y a pas de façon belaran@999: utile pour fusionner votre travail. En fait, il n'y a pas de moyen belaran@999: utile de montrer que les différences sont faites à partir de votre belaran@999: vision des modifications. belaran@999: belaran@999: Il y a ainsi quelques recommandations claires sur les belaran@999: types de fichiers spécifiques avec lesquels faire très belaran@999: attention. belaran@999: belaran@999: belaran@999: Les fichier qui sont très gros et belaran@999: incompressibles, comme les images ISO de CD-ROM, sont, par belaran@999: construction très gros et les cloner à travers un réseau sera très belaran@999: long. belaran@999: belaran@999: Les fichiers qui changent beaucoup d'une belaran@999: révision à l'autre peuvent être très chers à sauvegarder si vous belaran@999: les éditez fréquemment, de même que les conflits entre deux éditions belaran@999: concurrentes peuvent être difficiles à résoudre. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Sauvegardes et miroirs belaran@999: belaran@999: Puisque Mercurial maintient une copie complète de belaran@999: l'historique de chaque clone, toute personne qui utilise Mercurial pour belaran@999: collaborer à un projet peut potentiellement agir comme une source de belaran@999: sauvegarde si une catastrophe survenait. Si un dépôt central devient belaran@999: indisponible, vous pouvez construire un remplaçant en clonant une copie belaran@999: du dépôt à partir d'un des contributeurs en récupérant (pull) tous les belaran@999: changements qui n'auraient pas été vus par les autres. belaran@999: belaran@999: Il est simple d'utiliser Mercurial pour construire des belaran@999: serveurs hors site de sauvegarde et des miroirs distants. Initiez une belaran@999: tâche périodique (ex. via la commande cron) sur un belaran@999: serveur distant pour récupérer (pull) les changements de votre dépôt belaran@999: distant chaque heure. Ceci sera difficile seulement dans le cas belaran@999: improbable où le nombre des dépôts maîtres que vous maintenez change belaran@999: souvent, auquel cas vous aurez besoin de faire un peu de scripting pour belaran@999: rafraichir la liste des dépôt à sauvegarder. belaran@999: belaran@999: Si vous exécutez des sauvegardes traditionnelles de belaran@999: votre dépôt maître sur bande ou disque, et que vous voulez sauvegarder belaran@999: un dépôt nommé myrepo, utilisez la commande belaran@999: hg clone -U myrepo myrepo.bak pour créer un clone de belaran@999: myrepo avant de commencer vos backups. belaran@999: L'option ne crée pas de répertoire de travail après belaran@999: que le clone soit accompli, puisque ceci serait superflu et ferait que belaran@999: la sauvegarde prenne plus de temps. belaran@999: belaran@999: Si vous voulez ensuite sauvegarder belaran@999: myrepo.bak au lieu de myrepo, belaran@999: vous aurez la garantie d'avoir une image (snapshot) consistante de belaran@999: votre dépôt sur lequel un développeur insomniaque n'enverra (push) pas de belaran@999: changements en milieu de sauvegarde. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Collaborating with other people belaran@999: belaran@999: As a completely decentralised tool, Mercurial doesn't impose belaran@999: any policy on how people ought to work with each other. However, belaran@999: if you're new to distributed revision control, it helps to have belaran@999: some tools and examples in mind when you're thinking about belaran@999: possible workflow models. belaran@999: belaran@999: belaran@999: Mercurial's web interface belaran@999: belaran@999: Mercurial has a powerful web interface that provides several belaran@999: useful capabilities. belaran@999: belaran@999: For interactive use, the web interface lets you browse a belaran@999: single repository or a collection of repositories. You can view belaran@999: the history of a repository, examine each change (comments and belaran@999: diffs), and view the contents of each directory and file. You belaran@999: can even get a view of history that gives a graphical view of belaran@999: the relationships between individual changes and merges. belaran@999: belaran@999: Also for human consumption, the web interface provides belaran@999: Atom and RSS feeds of the changes in a repository. This lets you belaran@999: subscribe to a repository using your favorite belaran@999: feed reader, and be automatically notified of activity in that belaran@999: repository as soon as it happens. I find this capability much belaran@999: more convenient than the model of subscribing to a mailing list belaran@999: to which notifications are sent, as it requires no additional belaran@999: configuration on the part of whoever is serving the belaran@999: repository. belaran@999: belaran@999: The web interface also lets remote users clone a repository, belaran@999: pull changes from it, and (when the server is configured to belaran@999: permit it) push changes back to it. Mercurial's HTTP tunneling belaran@999: protocol aggressively compresses data, so that it works belaran@999: efficiently even over low-bandwidth network connections. belaran@999: belaran@999: The easiest way to get started with the web interface is to belaran@999: use your web browser to visit an existing repository, such as belaran@999: the master Mercurial repository at http://www.selenic.com/repo/hg. belaran@999: belaran@999: If you're interested in providing a web interface belaran@999: to your own repositories, there are several good ways to do belaran@999: this. belaran@999: belaran@999: The easiest and fastest way to get started in an informal belaran@999: environment is to use the hg belaran@999: serve command, which is best suited to short-term belaran@999: lightweight serving. See below for details of how to use belaran@999: this command. belaran@999: belaran@999: For longer-lived repositories that you'd like to belaran@999: have permanently available, there are several public hosting belaran@999: services available. Some are free to open source projects, belaran@999: while others offer paid commercial hosting. An up-to-date list belaran@999: is available at http://www.selenic.com/mercurial/wiki/index.cgi/MercurialHosting. belaran@999: belaran@999: If you would prefer to host your own repositories, Mercurial belaran@999: has built-in support for several popular hosting technologies, belaran@999: most notably CGI (Common Gateway Interface), and WSGI (Web belaran@999: Services Gateway Interface). See for details of CGI and WSGI belaran@999: configuration. belaran@999: belaran@999: belaran@999: belaran@999: Collaboration models belaran@999: belaran@999: With a suitably flexible tool, making decisions about belaran@999: workflow is much more of a social engineering challenge than a belaran@999: technical one. Mercurial imposes few limitations on how you can belaran@999: structure the flow of work in a project, so it's up to you and belaran@999: your group to set up and live with a model that matches your own belaran@999: particular needs. belaran@999: belaran@999: belaran@999: Factors to keep in mind belaran@999: belaran@999: The most important aspect of any model that you must keep belaran@999: in mind is how well it matches the needs and capabilities of belaran@999: the people who will be using it. This might seem belaran@999: self-evident; even so, you still can't afford to forget it for belaran@999: a moment. belaran@999: belaran@999: I once put together a workflow model that seemed to make belaran@999: perfect sense to me, but that caused a considerable amount of belaran@999: consternation and strife within my development team. In spite belaran@999: of my attempts to explain why we needed a complex set of belaran@999: branches, and how changes ought to flow between them, a few belaran@999: team members revolted. Even though they were smart people, belaran@999: they didn't want to pay attention to the constraints we were belaran@999: operating under, or face the consequences of those constraints belaran@999: in the details of the model that I was advocating. belaran@999: belaran@999: Don't sweep foreseeable social or technical problems under belaran@999: the rug. Whatever scheme you put into effect, you should plan belaran@999: for mistakes and problem scenarios. Consider adding automated belaran@999: machinery to prevent, or quickly recover from, trouble that belaran@999: you can anticipate. As an example, if you intend to have a belaran@999: branch with not-for-release changes in it, you'd do well to belaran@999: think early about the possibility that someone might belaran@999: accidentally merge those changes into a release branch. You belaran@999: could avoid this particular problem by writing a hook that belaran@999: prevents changes from being merged from an inappropriate belaran@999: branch. belaran@999: belaran@999: belaran@999: belaran@999: Informal anarchy belaran@999: belaran@999: I wouldn't suggest an anything goes belaran@999: approach as something sustainable, but it's a model that's belaran@999: easy to grasp, and it works perfectly well in a few unusual belaran@999: situations. belaran@999: belaran@999: As one example, many projects have a loose-knit group of belaran@999: collaborators who rarely physically meet each other. Some belaran@999: groups like to overcome the isolation of working at a distance belaran@999: by organizing occasional sprints. In a sprint, belaran@999: a number of people get together in a single location (a belaran@999: company's conference room, a hotel meeting room, that kind of belaran@999: place) and spend several days more or less locked in there, belaran@999: hacking intensely on a handful of projects. belaran@999: belaran@999: A sprint or a hacking session in a coffee shop are the perfect places to use the belaran@999: hg serve command, since belaran@999: hg serve does not require any belaran@999: fancy server infrastructure. You can get started with belaran@999: hg serve in moments, by belaran@999: reading below. Then simply belaran@999: tell the person next to you that you're running a server, send belaran@999: the URL to them in an instant message, and you immediately belaran@999: have a quick-turnaround way to work together. They can type belaran@999: your URL into their web browser and quickly review your belaran@999: changes; or they can pull a bugfix from you and verify it; or belaran@999: they can clone a branch containing a new feature and try it belaran@999: out. belaran@999: belaran@999: The charm, and the problem, with doing things belaran@999: in an ad hoc fashion like this is that only people who know belaran@999: about your changes, and where they are, can see them. Such an belaran@999: informal approach simply doesn't scale beyond a handful belaran@999: people, because each individual needs to know about belaran@999: n different repositories to pull belaran@999: from. belaran@999: belaran@999: belaran@999: belaran@999: A single central repository belaran@999: belaran@999: For smaller projects migrating from a centralised revision belaran@999: control tool, perhaps the easiest way to get started is to belaran@999: have changes flow through a single shared central repository. belaran@999: This is also the most common building block for belaran@999: more ambitious workflow schemes. belaran@999: belaran@999: Contributors start by cloning a copy of this repository. belaran@999: They can pull changes from it whenever they need to, and some belaran@999: (perhaps all) developers have permission to push a change back belaran@999: when they're ready for other people to see it. belaran@999: belaran@999: Under this model, it can still often make sense for people belaran@999: to pull changes directly from each other, without going belaran@999: through the central repository. Consider a case in which I belaran@999: have a tentative bug fix, but I am worried that if I were to belaran@999: publish it to the central repository, it might subsequently belaran@999: break everyone else's trees as they pull it. To reduce the belaran@999: potential for damage, I can ask you to clone my repository belaran@999: into a temporary repository of your own and test it. This belaran@999: lets us put off publishing the potentially unsafe change until belaran@999: it has had a little testing. belaran@999: belaran@999: If a team is hosting its own repository in this belaran@999: kind of scenario, people will usually use the belaran@999: ssh protocol to securely push changes to belaran@999: the central repository, as documented in . It's also usual to publish a belaran@999: read-only copy of the repository over HTTP, as in belaran@999: . Publishing over HTTP belaran@999: satisfies the needs of people who don't have push access, and belaran@999: those who want to use web browsers to browse the repository's belaran@999: history. belaran@999: belaran@999: belaran@999: belaran@999: A hosted central repository belaran@999: belaran@999: A wonderful thing about public hosting services like belaran@999: Bitbucket is that belaran@999: not only do they handle the fiddly server configuration belaran@999: details, such as user accounts, authentication, and secure belaran@999: wire protocols, they provide additional infrastructure to make belaran@999: this model work well. belaran@999: belaran@999: For instance, a well-engineered hosting service will let belaran@999: people clone their own copies of a repository with a single belaran@999: click. This lets people work in separate spaces and share belaran@999: their changes when they're ready. belaran@999: belaran@999: In addition, a good hosting service will let people belaran@999: communicate with each other, for instance to say there belaran@999: are changes ready for you to review in this belaran@999: tree. belaran@999: belaran@999: belaran@999: belaran@999: Working with multiple branches belaran@999: belaran@999: Projects of any significant size naturally tend to make belaran@999: progress on several fronts simultaneously. In the case of belaran@999: software, it's common for a project to go through periodic belaran@999: official releases. A release might then go into belaran@999: maintenance mode for a while after its first belaran@999: publication; maintenance releases tend to contain only bug belaran@999: fixes, not new features. In parallel with these maintenance belaran@999: releases, one or more future releases may be under belaran@999: development. People normally use the word belaran@999: branch to refer to one of these many slightly belaran@999: different directions in which development is belaran@999: proceeding. belaran@999: belaran@999: Mercurial is particularly well suited to managing a number belaran@999: of simultaneous, but not identical, branches. Each belaran@999: development direction can live in its own belaran@999: central repository, and you can merge changes from one to belaran@999: another as the need arises. Because repositories are belaran@999: independent of each other, unstable changes in a development belaran@999: branch will never affect a stable branch unless someone belaran@999: explicitly merges those changes into the stable branch. belaran@999: belaran@999: Here's an example of how this can work in practice. Let's belaran@999: say you have one main branch on a central belaran@999: server. belaran@999: belaran@999: belaran@999: $ hg init main belaran@999: $ cd main belaran@999: $ echo 'This is a boring feature.' > myfile belaran@999: $ hg commit -A -m 'We have reached an important milestone!' belaran@999: adding myfile belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: People clone it, make changes locally, test them, and push belaran@999: them back. belaran@999: belaran@999: Once the main branch reaches a release milestone, you can belaran@999: use the hg tag command to belaran@999: give a permanent name to the milestone revision. belaran@999: belaran@999: belaran@999: $ hg tag v1.0 belaran@999: $ hg tip belaran@999: changeset: 1:5e447fdaf941 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:47 2009 +0000 belaran@999: summary: Added tag v1.0 for changeset 6412b791fd06 belaran@999: belaran@999: $ hg tags belaran@999: tip 1:5e447fdaf941 belaran@999: v1.0 0:6412b791fd06 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Let's say some ongoing belaran@999: development occurs on the main branch. belaran@999: belaran@999: belaran@999: $ cd ../main belaran@999: $ echo 'This is exciting and new!' >> myfile belaran@999: $ hg commit -m 'Add a new feature' belaran@999: $ cat myfile belaran@999: This is a boring feature. belaran@999: This is exciting and new! belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Using the tag that was recorded at the milestone, people belaran@999: who clone that repository at any time in the future can use belaran@999: hg update to get a copy of belaran@999: the working directory exactly as it was when that tagged belaran@999: revision was committed. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone -U main main-old belaran@999: $ cd main-old belaran@999: $ hg update v1.0 belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cat myfile belaran@999: This is a boring feature. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: In addition, immediately after the main branch is tagged, belaran@999: we can then clone the main branch on the server to a new belaran@999: stable branch, also on the server. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone -rv1.0 main stable belaran@999: requesting all changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If we need to make a change to the stable belaran@999: branch, we can then clone that belaran@999: repository, make our changes, commit, and push our changes belaran@999: back there. belaran@999: belaran@999: belaran@999: $ hg clone stable stable-fix belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd stable-fix belaran@999: $ echo 'This is a fix to a boring feature.' > myfile belaran@999: $ hg commit -m 'Fix a bug' belaran@999: $ hg push belaran@999: pushing to /tmp/branchingPsTziR/stable belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Because Mercurial repositories are independent, and belaran@999: Mercurial doesn't move changes around automatically, the belaran@999: stable and main branches are isolated belaran@999: from each other. The changes that we made on the main branch belaran@999: don't leak to the stable branch, and vice belaran@999: versa. belaran@999: belaran@999: We'll often want all of our bugfixes on the stable belaran@999: branch to show up on the main branch, too. Rather than belaran@999: rewrite a bugfix on the main branch, we can simply pull and belaran@999: merge changes from the stable to the main branch, and belaran@999: Mercurial will bring those bugfixes in for us. belaran@999: belaran@999: belaran@999: $ cd ../main belaran@999: $ hg pull ../stable belaran@999: pulling from ../stable belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files (+1 heads) belaran@999: (run 'hg heads' to see heads, 'hg merge' to merge) belaran@999: $ hg merge belaran@999: merging myfile belaran@999: 0 files updated, 1 files merged, 0 files removed, 0 files unresolved belaran@999: (branch merge, don't forget to commit) belaran@999: $ hg commit -m 'Bring in bugfix from stable branch' belaran@999: $ cat myfile belaran@999: This is a fix to a boring feature. belaran@999: This is exciting and new! belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The main branch will still contain changes that belaran@999: are not on the stable branch, but it will also contain all of belaran@999: the bugfixes from the stable branch. The stable branch belaran@999: remains unaffected by these changes, since changes are only belaran@999: flowing from the stable to the main branch, and not the other belaran@999: way. belaran@999: belaran@999: belaran@999: belaran@999: Feature branches belaran@999: belaran@999: For larger projects, an effective way to manage change is belaran@999: to break up a team into smaller groups. Each group has a belaran@999: shared branch of its own, cloned from a single belaran@999: master branch used by the entire project. belaran@999: People working on an individual branch are typically quite belaran@999: isolated from developments on other branches. belaran@999: belaran@999:
belaran@999: Feature branches belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: When a particular feature is deemed to be in suitable belaran@999: shape, someone on that feature team pulls and merges from the belaran@999: master branch into the feature branch, then pushes back up to belaran@999: the master branch. belaran@999:
belaran@999: belaran@999: belaran@999: The release train belaran@999: belaran@999: Some projects are organized on a train belaran@999: basis: a release is scheduled to happen every few months, and belaran@999: whatever features are ready when the train is belaran@999: ready to leave are allowed in. belaran@999: belaran@999: This model resembles working with feature branches. The belaran@999: difference is that when a feature branch misses a train, belaran@999: someone on the feature team pulls and merges the changes that belaran@999: went out on that train release into the feature branch, and belaran@999: the team continues its work on top of that release so that belaran@999: their feature can make the next release. belaran@999: belaran@999: belaran@999: belaran@999: The Linux kernel model belaran@999: belaran@999: The development of the Linux kernel has a shallow belaran@999: hierarchical structure, surrounded by a cloud of apparent belaran@999: chaos. Because most Linux developers use belaran@999: git, a distributed revision control tool belaran@999: with capabilities similar to Mercurial, it's useful to belaran@999: describe the way work flows in that environment; if you like belaran@999: the ideas, the approach translates well across tools. belaran@999: belaran@999: At the center of the community sits Linus Torvalds, the belaran@999: creator of Linux. He publishes a single source repository belaran@999: that is considered the authoritative current belaran@999: tree by the entire developer community. Anyone can clone belaran@999: Linus's tree, but he is very choosy about whose trees he pulls belaran@999: from. belaran@999: belaran@999: Linus has a number of trusted lieutenants. belaran@999: As a general rule, he pulls whatever changes they publish, in belaran@999: most cases without even reviewing those changes. Some of belaran@999: those lieutenants are generally agreed to be belaran@999: maintainers, responsible for specific belaran@999: subsystems within the kernel. If a random kernel hacker wants belaran@999: to make a change to a subsystem that they want to end up in belaran@999: Linus's tree, they must find out who the subsystem's belaran@999: maintainer is, and ask that maintainer to take their change. belaran@999: If the maintainer reviews their changes and agrees to take belaran@999: them, they'll pass them along to Linus in due course. belaran@999: belaran@999: Individual lieutenants have their own approaches to belaran@999: reviewing, accepting, and publishing changes; and for deciding belaran@999: when to feed them to Linus. In addition, there are several belaran@999: well known branches that people use for different purposes. belaran@999: For example, a few people maintain stable belaran@999: repositories of older versions of the kernel, to which they belaran@999: apply critical fixes as needed. Some maintainers publish belaran@999: multiple trees: one for experimental changes; one for changes belaran@999: that they are about to feed upstream; and so on. Others just belaran@999: publish a single tree. belaran@999: belaran@999: This model has two notable features. The first is that belaran@999: it's pull only. You have to ask, convince, or belaran@999: beg another developer to take a change from you, because there belaran@999: are almost no trees to which more than one person can push, belaran@999: and there's no way to push changes into a tree that someone belaran@999: else controls. belaran@999: belaran@999: The second is that it's based on reputation and acclaim. belaran@999: If you're an unknown, Linus will probably ignore changes from belaran@999: you without even responding. But a subsystem maintainer will belaran@999: probably review them, and will likely take them if they pass belaran@999: their criteria for suitability. The more good belaran@999: changes you contribute to a maintainer, the more likely they belaran@999: are to trust your judgment and accept your changes. If you're belaran@999: well-known and maintain a long-lived branch for something belaran@999: Linus hasn't yet accepted, people with similar interests may belaran@999: pull your changes regularly to keep up with your work. belaran@999: belaran@999: Reputation and acclaim don't necessarily cross subsystem belaran@999: or people boundaries. If you're a respected belaran@999: but specialised storage hacker, and you try to fix a belaran@999: networking bug, that change will receive a level of scrutiny belaran@999: from a network maintainer comparable to a change from a belaran@999: complete stranger. belaran@999: belaran@999: To people who come from more orderly project backgrounds, belaran@999: the comparatively chaotic Linux kernel development process belaran@999: often seems completely insane. It's subject to the whims of belaran@999: individuals; people make sweeping changes whenever they deem belaran@999: it appropriate; and the pace of development is astounding. belaran@999: And yet Linux is a highly successful, well-regarded piece of belaran@999: software. belaran@999: belaran@999: belaran@999: belaran@999: Pull-only versus shared-push collaboration belaran@999: belaran@999: A perpetual source of heat in the open source community is belaran@999: whether a development model in which people only ever pull belaran@999: changes from others is better than one in which belaran@999: multiple people can push changes to a shared belaran@999: repository. belaran@999: belaran@999: Typically, the backers of the shared-push model use tools belaran@999: that actively enforce this approach. If you're using a belaran@999: centralised revision control tool such as Subversion, there's belaran@999: no way to make a choice over which model you'll use: the tool belaran@999: gives you shared-push, and if you want to do anything else, belaran@999: you'll have to roll your own approach on top (such as applying belaran@999: a patch by hand). belaran@999: belaran@999: A good distributed revision control tool will belaran@999: support both models. You and your collaborators can then belaran@999: structure how you work together based on your own needs and belaran@999: preferences, not on what contortions your tools force you belaran@999: into. belaran@999: belaran@999: belaran@999: Where collaboration meets branch management belaran@999: belaran@999: Once you and your team set up some shared belaran@999: repositories and start propagating changes back and forth belaran@999: between local and shared repos, you begin to face a related, belaran@999: but slightly different challenge: that of managing the belaran@999: multiple directions in which your team may be moving at once. belaran@999: Even though this subject is intimately related to how your belaran@999: team collaborates, it's dense enough to merit treatment of its belaran@999: own, in . belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: The technical side of sharing belaran@999: belaran@999: The remainder of this chapter is devoted to the question of belaran@999: sharing changes with your collaborators. belaran@999: belaran@999: belaran@999: belaran@999: Informal sharing with <command role="hg-cmd" moreinfo="none">hg belaran@999: serve</command> belaran@999: belaran@999: Mercurial's hg serve belaran@999: command is wonderfully suited to small, tight-knit, and belaran@999: fast-paced group environments. It also provides a great way to belaran@999: get a feel for using Mercurial commands over a network. belaran@999: belaran@999: Run hg serve inside a belaran@999: repository, and in under a second it will bring up a specialised belaran@999: HTTP server; this will accept connections from any client, and belaran@999: serve up data for that repository until you terminate it. belaran@999: Anyone who knows the URL of the server you just started, and can belaran@999: talk to your computer over the network, can then use a web belaran@999: browser or Mercurial to read data from that repository. A URL belaran@999: for a hg serve instance running belaran@999: on a laptop is likely to look something like belaran@999: http://my-laptop.local:8000/. belaran@999: belaran@999: The hg serve command is belaran@999: not a general-purpose web server. It can do belaran@999: only two things: belaran@999: belaran@999: Allow people to browse the history of the belaran@999: repository it's serving, from their normal web belaran@999: browsers. belaran@999: belaran@999: Speak Mercurial's wire protocol, so that people belaran@999: can hg clone or hg pull changes from that belaran@999: repository. belaran@999: belaran@999: In particular, hg serve belaran@999: won't allow remote users to modify your belaran@999: repository. It's intended for read-only use. belaran@999: belaran@999: If you're getting started with Mercurial, there's nothing to belaran@999: prevent you from using hg serve belaran@999: to serve up a repository on your own computer, then use commands belaran@999: like hg clone, hg incoming, and so on to talk to that belaran@999: server as if the repository was hosted remotely. This can help belaran@999: you to quickly get acquainted with using commands on belaran@999: network-hosted repositories. belaran@999: belaran@999: belaran@999: A few things to keep in mind belaran@999: belaran@999: Because it provides unauthenticated read access to all belaran@999: clients, you should only use hg belaran@999: serve in an environment where you either don't belaran@999: care, or have complete control over, who can access your belaran@999: network and pull data from your repository. belaran@999: belaran@999: The hg serve command belaran@999: knows nothing about any firewall software you might have belaran@999: installed on your system or network. It cannot detect or belaran@999: control your firewall software. If other people are unable to belaran@999: talk to a running hg serve belaran@999: instance, the second thing you should do belaran@999: (after you make sure that they're using belaran@999: the correct URL) is check your firewall configuration. belaran@999: belaran@999: By default, hg serve belaran@999: listens for incoming connections on port 8000. If another belaran@999: process is already listening on the port you want to use, you belaran@999: can specify a different port to listen on using the option. belaran@999: belaran@999: Normally, when hg serve belaran@999: starts, it prints no output, which can be a bit unnerving. If belaran@999: you'd like to confirm that it is indeed running correctly, and belaran@999: find out what URL you should send to your collaborators, start belaran@999: it with the belaran@999: option. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Using the Secure Shell (ssh) protocol belaran@999: belaran@999: You can pull and push changes securely over a network belaran@999: connection using the Secure Shell (ssh) belaran@999: protocol. To use this successfully, you may have to do a little belaran@999: bit of configuration on the client or server sides. belaran@999: belaran@999: If you're not familiar with ssh, it's the name of belaran@999: both a command and a network protocol that let you securely belaran@999: communicate with another computer. To use it with Mercurial, belaran@999: you'll be setting up one or more user accounts on a server so belaran@999: that remote users can log in and execute commands. belaran@999: belaran@999: (If you are familiar with ssh, you'll belaran@999: probably find some of the material that follows to be elementary belaran@999: in nature.) belaran@999: belaran@999: belaran@999: How to read and write ssh URLs belaran@999: belaran@999: An ssh URL tends to look like this: belaran@999: ssh://bos@hg.serpentine.com:22/hg/hgbook belaran@999: belaran@999: The ssh:// belaran@999: part tells Mercurial to use the ssh protocol. belaran@999: belaran@999: The bos@ belaran@999: component indicates what username to log into the server belaran@999: as. You can leave this out if the remote username is the belaran@999: same as your local username. belaran@999: belaran@999: The belaran@999: hg.serpentine.com gives belaran@999: the hostname of the server to log into. belaran@999: belaran@999: The :22 identifies the port belaran@999: number to connect to the server on. The default port is belaran@999: 22, so you only need to specify a colon and port number if belaran@999: you're not using port 22. belaran@999: belaran@999: The remainder of the URL is the local path to belaran@999: the repository on the server. belaran@999: belaran@999: belaran@999: There's plenty of scope for confusion with the path belaran@999: component of ssh URLs, as there is no standard way for tools belaran@999: to interpret it. Some programs behave differently than others belaran@999: when dealing with these paths. This isn't an ideal situation, belaran@999: but it's unlikely to change. Please read the following belaran@999: paragraphs carefully. belaran@999: belaran@999: Mercurial treats the path to a repository on the server as belaran@999: relative to the remote user's home directory. For example, if belaran@999: user foo on the server has a home directory belaran@999: of /home/foo, then an belaran@999: ssh URL that contains a path component of bar really belaran@999: refers to the directory /home/foo/bar. belaran@999: belaran@999: If you want to specify a path relative to another user's belaran@999: home directory, you can use a path that starts with a tilde belaran@999: character followed by the user's name (let's call them belaran@999: otheruser), like this. belaran@999: ssh://server/~otheruser/hg/repo belaran@999: belaran@999: And if you really want to specify an belaran@999: absolute path on the server, begin the belaran@999: path component with two slashes, as in this example. belaran@999: ssh://server//absolute/path belaran@999: belaran@999: belaran@999: belaran@999: Finding an ssh client for your system belaran@999: belaran@999: Almost every Unix-like system comes with OpenSSH belaran@999: preinstalled. If you're using such a system, run belaran@999: which ssh to find out if the belaran@999: ssh command is installed (it's usually in belaran@999: /usr/bin). In the belaran@999: unlikely event that it isn't present, take a look at your belaran@999: system documentation to figure out how to install it. belaran@999: belaran@999: On Windows, the TortoiseHg package is bundled belaran@999: with a version of Simon Tatham's excellent belaran@999: plink command, and you should not need to belaran@999: do any further configuration. belaran@999: belaran@999: belaran@999: belaran@999: Generating a key pair belaran@999: belaran@999: To avoid the need to repetitively type a belaran@999: password every time you need to use your ssh client, I belaran@999: recommend generating a key pair. belaran@999: belaran@999: belaran@999: Key pairs are not mandatory belaran@999: belaran@999: Mercurial knows nothing about ssh authentication or key belaran@999: pairs. You can, if you like, safely ignore this section and belaran@999: the one that follows until you grow tired of repeatedly belaran@999: typing ssh passwords. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: On a Unix-like system, the belaran@999: ssh-keygen command will do the belaran@999: trick. belaran@999: On Windows, if you're using TortoiseHg, you may need belaran@999: to download a command named puttygen belaran@999: from the belaran@999: PuTTY web site to generate a key pair. See belaran@999: the belaran@999: puttygen documentation for belaran@999: details of how use the command. belaran@999: belaran@999: belaran@999: belaran@999: When you generate a key pair, it's usually belaran@999: highly advisable to protect it with a belaran@999: passphrase. (The only time that you might not want to do this belaran@999: is when you're using the ssh protocol for automated tasks on a belaran@999: secure network.) belaran@999: belaran@999: Simply generating a key pair isn't enough, however. belaran@999: You'll need to add the public key to the set of authorised belaran@999: keys for whatever user you're logging in remotely as. For belaran@999: servers using OpenSSH (the vast majority), this will mean belaran@999: adding the public key to a list in a file called authorized_keys in their .ssh belaran@999: directory. belaran@999: belaran@999: On a Unix-like system, your public key will have a belaran@999: .pub extension. If you're using belaran@999: puttygen on Windows, you can save the belaran@999: public key to a file of your choosing, or paste it from the belaran@999: window it's displayed in straight into the authorized_keys file. belaran@999: belaran@999: belaran@999: Using an authentication agent belaran@999: belaran@999: An authentication agent is a daemon that stores belaran@999: passphrases in memory (so it will forget passphrases if you belaran@999: log out and log back in again). An ssh client will notice if belaran@999: it's running, and query it for a passphrase. If there's no belaran@999: authentication agent running, or the agent doesn't store the belaran@999: necessary passphrase, you'll have to type your passphrase belaran@999: every time Mercurial tries to communicate with a server on belaran@999: your behalf (e.g. whenever you pull or push changes). belaran@999: belaran@999: The downside of storing passphrases in an agent is that belaran@999: it's possible for a well-prepared attacker to recover the belaran@999: plain text of your passphrases, in some cases even if your belaran@999: system has been power-cycled. You should make your own belaran@999: judgment as to whether this is an acceptable risk. It belaran@999: certainly saves a lot of repeated typing. belaran@999: belaran@999: belaran@999: belaran@999: On Unix-like systems, the agent is called belaran@999: ssh-agent, and it's often run belaran@999: automatically for you when you log in. You'll need to use belaran@999: the ssh-add command to add passphrases belaran@999: to the agent's store. belaran@999: belaran@999: belaran@999: On Windows, if you're using TortoiseHg, the belaran@999: pageant command acts as the agent. As belaran@999: with puttygen, you'll need to download belaran@999: pageant from the PuTTY web belaran@999: site and read its belaran@999: documentation. The pageant belaran@999: command adds an icon to your system tray that will let you belaran@999: manage stored passphrases. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Configuring the server side properly belaran@999: belaran@999: Because ssh can be fiddly to set up if you're new to it, belaran@999: a variety of things can go wrong. Add Mercurial belaran@999: on top, and there's plenty more scope for head-scratching. belaran@999: Most of these potential problems occur on the server side, not belaran@999: the client side. The good news is that once you've gotten a belaran@999: configuration working, it will usually continue to work belaran@999: indefinitely. belaran@999: belaran@999: Before you try using Mercurial to talk to an ssh server, belaran@999: it's best to make sure that you can use the normal belaran@999: ssh or putty command to belaran@999: talk to the server first. If you run into problems with using belaran@999: these commands directly, Mercurial surely won't work. Worse, belaran@999: it will obscure the underlying problem. Any time you want to belaran@999: debug ssh-related Mercurial problems, you should drop back to belaran@999: making sure that plain ssh client commands work first, belaran@999: before you worry about whether there's a belaran@999: problem with Mercurial. belaran@999: belaran@999: The first thing to be sure of on the server side is that belaran@999: you can actually log in from another machine at all. If you belaran@999: can't use ssh or putty belaran@999: to log in, the error message you get may give you a few hints belaran@999: as to what's wrong. The most common problems are as belaran@999: follows. belaran@999: belaran@999: If you get a connection refused belaran@999: error, either there isn't an SSH daemon running on the belaran@999: server at all, or it's inaccessible due to firewall belaran@999: configuration. belaran@999: belaran@999: If you get a no route to host belaran@999: error, you either have an incorrect address for the server belaran@999: or a seriously locked down firewall that won't admit its belaran@999: existence at all. belaran@999: belaran@999: If you get a permission denied belaran@999: error, you may have mistyped the username on the server, belaran@999: or you could have mistyped your key's passphrase or the belaran@999: remote user's password. belaran@999: belaran@999: In summary, if you're having trouble talking to the belaran@999: server's ssh daemon, first make sure that one is running at belaran@999: all. On many systems it will be installed, but disabled, by belaran@999: default. Once you're done with this step, you should then belaran@999: check that the server's firewall is configured to allow belaran@999: incoming connections on the port the ssh daemon is listening belaran@999: on (usually 22). Don't worry about more exotic possibilities belaran@999: for misconfiguration until you've checked these two belaran@999: first. belaran@999: belaran@999: If you're using an authentication agent on the client side belaran@999: to store passphrases for your keys, you ought to be able to belaran@999: log into the server without being prompted for a passphrase or belaran@999: a password. If you're prompted for a passphrase, there are a belaran@999: few possible culprits. belaran@999: belaran@999: You might have forgotten to use belaran@999: ssh-add or pageant belaran@999: to store the passphrase. belaran@999: belaran@999: You might have stored the passphrase for the belaran@999: wrong key. belaran@999: belaran@999: If you're being prompted for the remote user's password, belaran@999: there are another few possible problems to check. belaran@999: belaran@999: Either the user's home directory or their belaran@999: .ssh belaran@999: directory might have excessively liberal permissions. As belaran@999: a result, the ssh daemon will not trust or read their belaran@999: authorized_keys file. belaran@999: For example, a group-writable home or .ssh belaran@999: directory will often cause this symptom. belaran@999: belaran@999: The user's authorized_keys file may have belaran@999: a problem. If anyone other than the user owns or can write belaran@999: to that file, the ssh daemon will not trust or read belaran@999: it. belaran@999: belaran@999: belaran@999: In the ideal world, you should be able to run the belaran@999: following command successfully, and it should print exactly belaran@999: one line of output, the current date and time. belaran@999: ssh myserver date belaran@999: belaran@999: If, on your server, you have login scripts that print belaran@999: banners or other junk even when running non-interactive belaran@999: commands like this, you should fix them before you continue, belaran@999: so that they only print output if they're run interactively. belaran@999: Otherwise these banners will at least clutter up Mercurial's belaran@999: output. Worse, they could potentially cause problems with belaran@999: running Mercurial commands remotely. Mercurial tries to belaran@999: detect and ignore banners in non-interactive belaran@999: ssh sessions, but it is not foolproof. (If belaran@999: you're editing your login scripts on your server, the usual belaran@999: way to see if a login script is running in an interactive belaran@999: shell is to check the return code from the command belaran@999: tty -s.) belaran@999: belaran@999: Once you've verified that plain old ssh is working with belaran@999: your server, the next step is to ensure that Mercurial runs on belaran@999: the server. The following command should run belaran@999: successfully: belaran@999: belaran@999: ssh myserver hg version belaran@999: belaran@999: If you see an error message instead of normal hg version output, this is usually belaran@999: because you haven't installed Mercurial to /usr/bin. Don't worry if this belaran@999: is the case; you don't need to do that. But you should check belaran@999: for a few possible problems. belaran@999: belaran@999: Is Mercurial really installed on the server at belaran@999: all? I know this sounds trivial, but it's worth belaran@999: checking! belaran@999: belaran@999: Maybe your shell's search path (usually set belaran@999: via the PATH environment variable) is belaran@999: simply misconfigured. belaran@999: belaran@999: Perhaps your PATH environment belaran@999: variable is only being set to point to the location of the belaran@999: hg executable if the login session is belaran@999: interactive. This can happen if you're setting the path belaran@999: in the wrong shell login script. See your shell's belaran@999: documentation for details. belaran@999: belaran@999: The PYTHONPATH environment belaran@999: variable may need to contain the path to the Mercurial belaran@999: Python modules. It might not be set at all; it could be belaran@999: incorrect; or it may be set only if the login is belaran@999: interactive. belaran@999: belaran@999: belaran@999: If you can run hg version belaran@999: over an ssh connection, well done! You've got the server and belaran@999: client sorted out. You should now be able to use Mercurial to belaran@999: access repositories hosted by that username on that server. belaran@999: If you run into problems with Mercurial and ssh at this point, belaran@999: try using the belaran@999: option to get a clearer picture of what's going on. belaran@999: belaran@999: belaran@999: Using compression with ssh belaran@999: belaran@999: Mercurial does not compress data when it uses the ssh belaran@999: protocol, because the ssh protocol can transparently compress belaran@999: data. However, the default behavior of ssh clients is belaran@999: not to request compression. belaran@999: belaran@999: Over any network other than a fast LAN (even a wireless belaran@999: network), using compression is likely to significantly speed belaran@999: up Mercurial's network operations. For example, over a WAN, belaran@999: someone measured compression as reducing the amount of time belaran@999: required to clone a particularly large repository from 51 belaran@999: minutes to 17 minutes. belaran@999: belaran@999: Both ssh and plink belaran@999: accept a option which belaran@999: turns on compression. You can easily edit your ~/.hgrc to enable compression for belaran@999: all of Mercurial's uses of the ssh protocol. Here is how to belaran@999: do so for regular ssh on Unix-like systems, belaran@999: for example. belaran@999: [ui] belaran@999: ssh = ssh -C belaran@999: belaran@999: If you use ssh on a belaran@999: Unix-like system, you can configure it to always use belaran@999: compression when talking to your server. To do this, edit belaran@999: your .ssh/config file belaran@999: (which may not yet exist), as follows. belaran@999: belaran@999: Host hg belaran@999: Compression yes belaran@999: HostName hg.example.com belaran@999: belaran@999: This defines a hostname alias, belaran@999: hg. When you use that hostname on the belaran@999: ssh command line or in a Mercurial belaran@999: ssh-protocol URL, it will cause belaran@999: ssh to connect to belaran@999: hg.example.com and use compression. This belaran@999: gives you both a shorter name to type and compression, each of belaran@999: which is a good thing in its own right. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Serving over HTTP using CGI belaran@999: belaran@999: The simplest way to host one or more repositories in a belaran@999: permanent way is to use a web server and Mercurial's CGI belaran@999: support. belaran@999: belaran@999: Depending on how ambitious you are, configuring Mercurial's belaran@999: CGI interface can take anything from a few moments to several belaran@999: hours. belaran@999: belaran@999: We'll begin with the simplest of examples, and work our way belaran@999: towards a more complex configuration. Even for the most basic belaran@999: case, you're almost certainly going to need to read and modify belaran@999: your web server's configuration. belaran@999: belaran@999: belaran@999: High pain tolerance required belaran@999: belaran@999: Configuring a web server is a complex, fiddly, belaran@999: and highly system-dependent activity. I can't possibly give belaran@999: you instructions that will cover anything like all of the belaran@999: cases you will encounter. Please use your discretion and belaran@999: judgment in following the sections below. Be prepared to make belaran@999: plenty of mistakes, and to spend a lot of time reading your belaran@999: server's error logs. belaran@999: belaran@999: If you don't have a strong stomach for tweaking belaran@999: configurations over and over, or a compelling need to host belaran@999: your own services, you might want to try one of the public belaran@999: hosting services that I mentioned earlier. belaran@999: belaran@999: belaran@999: belaran@999: Web server configuration checklist belaran@999: belaran@999: Before you continue, do take a few moments to check a few belaran@999: aspects of your system's setup. belaran@999: belaran@999: belaran@999: Do you have a web server installed belaran@999: at all? Mac OS X and some Linux distributions ship with belaran@999: Apache, but many other systems may not have a web server belaran@999: installed. belaran@999: belaran@999: If you have a web server installed, is it belaran@999: actually running? On most systems, even if one is belaran@999: present, it will be disabled by default. belaran@999: belaran@999: Is your server configured to allow you to run belaran@999: CGI programs in the directory where you plan to do so? belaran@999: Most servers default to explicitly disabling the ability belaran@999: to run CGI programs. belaran@999: belaran@999: belaran@999: If you don't have a web server installed, and don't have belaran@999: substantial experience configuring Apache, you should consider belaran@999: using the lighttpd web server instead of belaran@999: Apache. Apache has a well-deserved reputation for baroque and belaran@999: confusing configuration. While lighttpd is belaran@999: less capable in some ways than Apache, most of these belaran@999: capabilities are not relevant to serving Mercurial belaran@999: repositories. And lighttpd is undeniably belaran@999: much easier to get started with than belaran@999: Apache. belaran@999: belaran@999: belaran@999: belaran@999: Basic CGI configuration belaran@999: belaran@999: On Unix-like systems, it's common for users to have a belaran@999: subdirectory named something like public_html in their home belaran@999: directory, from which they can serve up web pages. A file belaran@999: named foo in this directory will be belaran@999: accessible at a URL of the form belaran@999: http://www.example.com/username/foo. belaran@999: belaran@999: To get started, find the hgweb.cgi script that should be belaran@999: present in your Mercurial installation. If you can't quickly belaran@999: find a local copy on your system, simply download one from the belaran@999: master Mercurial repository at http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi. belaran@999: belaran@999: You'll need to copy this script into your public_html directory, and belaran@999: ensure that it's executable. belaran@999: cp .../hgweb.cgi ~/public_html belaran@999: chmod 755 ~/public_html/hgweb.cgi belaran@999: The 755 argument to belaran@999: chmod is a little more general than just belaran@999: making the script executable: it ensures that the script is belaran@999: executable by anyone, and that group and belaran@999: other write permissions are belaran@999: not set. If you were to leave those belaran@999: write permissions enabled, Apache's suexec belaran@999: subsystem would likely refuse to execute the script. In fact, belaran@999: suexec also insists that the belaran@999: directory in which the script resides belaran@999: must not be writable by others. belaran@999: chmod 755 ~/public_html belaran@999: belaran@999: belaran@999: What could <emphasis>possibly</emphasis> go belaran@999: wrong? belaran@999: belaran@999: Once you've copied the CGI script into place, belaran@999: go into a web browser, and try to open the URL belaran@999: http://myhostname/~myuser/hgweb.cgi, belaran@999: but brace yourself for instant failure. belaran@999: There's a high probability that trying to visit this URL belaran@999: will fail, and there are many possible reasons for this. In belaran@999: fact, you're likely to stumble over almost every one of the belaran@999: possible errors below, so please read carefully. The belaran@999: following are all of the problems I ran into on a system belaran@999: running Fedora 7, with a fresh installation of Apache, and a belaran@999: user account that I created specially to perform this belaran@999: exercise. belaran@999: belaran@999: Your web server may have per-user directories disabled. belaran@999: If you're using Apache, search your config file for a belaran@999: UserDir directive. If there's none belaran@999: present, per-user directories will be disabled. If one belaran@999: exists, but its value is disabled, then belaran@999: per-user directories will be disabled. Otherwise, the belaran@999: string after UserDir gives the name of belaran@999: the subdirectory that Apache will look in under your home belaran@999: directory, for example public_html. belaran@999: belaran@999: Your file access permissions may be too restrictive. belaran@999: The web server must be able to traverse your home directory belaran@999: and directories under your public_html directory, and belaran@999: read files under the latter too. Here's a quick recipe to belaran@999: help you to make your permissions more appropriate. belaran@999: chmod 755 ~ belaran@999: find ~/public_html -type d -print0 | xargs -0r chmod 755 belaran@999: find ~/public_html -type f -print0 | xargs -0r chmod 644 belaran@999: belaran@999: The other possibility with permissions is that you might belaran@999: get a completely empty window when you try to load the belaran@999: script. In this case, it's likely that your access belaran@999: permissions are too permissive. Apache's belaran@999: suexec subsystem won't execute a script belaran@999: that's group- or world-writable, for example. belaran@999: belaran@999: Your web server may be configured to disallow execution belaran@999: of CGI programs in your per-user web directory. Here's belaran@999: Apache's default per-user configuration from my Fedora belaran@999: system. belaran@999: belaran@999: belaran@999: <Directory /home/*/public_html> belaran@999: AllowOverride FileInfo AuthConfig Limit belaran@999: Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec belaran@999: <Limit GET POST OPTIONS> belaran@999: Order allow,deny belaran@999: Allow from all belaran@999: </Limit> belaran@999: <LimitExcept GET POST OPTIONS> belaran@999: Order deny,allow Deny from all belaran@999: </LimitExcept> belaran@999: </Directory> belaran@999: belaran@999: belaran@999: belaran@999: If you find a similar-looking belaran@999: Directory group in your Apache belaran@999: configuration, the directive to look at inside it is belaran@999: Options. Add ExecCGI belaran@999: to the end of this list if it's missing, and restart the web belaran@999: server. belaran@999: belaran@999: If you find that Apache serves you the text of the CGI belaran@999: script instead of executing it, you may need to either belaran@999: uncomment (if already present) or add a directive like belaran@999: this. belaran@999: AddHandler cgi-script .cgi belaran@999: belaran@999: The next possibility is that you might be served with a belaran@999: colourful Python backtrace claiming that it can't import a belaran@999: mercurial-related module. This is belaran@999: actually progress! The server is now capable of executing belaran@999: your CGI script. This error is only likely to occur if belaran@999: you're running a private installation of Mercurial, instead belaran@999: of a system-wide version. Remember that the web server runs belaran@999: the CGI program without any of the environment variables belaran@999: that you take for granted in an interactive session. If belaran@999: this error happens to you, edit your copy of hgweb.cgi and follow the belaran@999: directions inside it to correctly set your belaran@999: PYTHONPATH environment variable. belaran@999: belaran@999: Finally, you are certain to be belaran@999: served with another colourful Python backtrace: this one belaran@999: will complain that it can't find /path/to/repository. Edit belaran@999: your hgweb.cgi script belaran@999: and replace the /path/to/repository string belaran@999: with the complete path to the repository you want to serve belaran@999: up. belaran@999: belaran@999: At this point, when you try to reload the page, you belaran@999: should be presented with a nice HTML view of your belaran@999: repository's history. Whew! belaran@999: belaran@999: belaran@999: belaran@999: Configuring lighttpd belaran@999: belaran@999: To be exhaustive in my experiments, I tried configuring belaran@999: the increasingly popular lighttpd web belaran@999: server to serve the same repository as I described with belaran@999: Apache above. I had already overcome all of the problems I belaran@999: outlined with Apache, many of which are not server-specific. belaran@999: As a result, I was fairly sure that my file and directory belaran@999: permissions were good, and that my hgweb.cgi script was properly belaran@999: edited. belaran@999: belaran@999: Once I had Apache running, getting belaran@999: lighttpd to serve the repository was a belaran@999: snap (in other words, even if you're trying to use belaran@999: lighttpd, you should read the Apache belaran@999: section). I first had to edit the belaran@999: mod_access section of its config file to belaran@999: enable mod_cgi and belaran@999: mod_userdir, both of which were disabled belaran@999: by default on my system. I then added a few lines to the belaran@999: end of the config file, to configure these modules. belaran@999: userdir.path = "public_html" belaran@999: cgi.assign = (".cgi" => "" ) belaran@999: With this done, lighttpd ran belaran@999: immediately for me. If I had configured belaran@999: lighttpd before Apache, I'd almost belaran@999: certainly have run into many of the same system-level belaran@999: configuration problems as I did with Apache. However, I belaran@999: found lighttpd to be noticeably easier to belaran@999: configure than Apache, even though I've used Apache for over belaran@999: a decade, and this was my first exposure to belaran@999: lighttpd. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Sharing multiple repositories with one CGI script belaran@999: belaran@999: The hgweb.cgi script belaran@999: only lets you publish a single repository, which is an belaran@999: annoying restriction. If you want to publish more than one belaran@999: without wracking yourself with multiple copies of the same belaran@999: script, each with different names, a better choice is to use belaran@999: the hgwebdir.cgi belaran@999: script. belaran@999: belaran@999: The procedure to configure hgwebdir.cgi is only a little more belaran@999: involved than for hgweb.cgi. First, you must obtain belaran@999: a copy of the script. If you don't have one handy, you can belaran@999: download a copy from the master Mercurial repository at http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi. belaran@999: belaran@999: You'll need to copy this script into your public_html directory, and belaran@999: ensure that it's executable. belaran@999: belaran@999: cp .../hgwebdir.cgi ~/public_html belaran@999: chmod 755 ~/public_html ~/public_html/hgwebdir.cgi belaran@999: belaran@999: With basic configuration out of the way, try to belaran@999: visit http://myhostname/~myuser/hgwebdir.cgi belaran@999: in your browser. It should belaran@999: display an empty list of repositories. If you get a blank belaran@999: window or error message, try walking through the list of belaran@999: potential problems in . belaran@999: belaran@999: The hgwebdir.cgi belaran@999: script relies on an external configuration file. By default, belaran@999: it searches for a file named hgweb.config in the same directory belaran@999: as itself. You'll need to create this file, and make it belaran@999: world-readable. The format of the file is similar to a belaran@999: Windows ini file, as understood by Python's belaran@999: ConfigParser belaran@999: web:configparser module. belaran@999: belaran@999: The easiest way to configure hgwebdir.cgi is with a section belaran@999: named collections. This will automatically belaran@999: publish every repository under the belaran@999: directories you name. The section should look like belaran@999: this: belaran@999: [collections] belaran@999: /my/root = /my/root belaran@999: Mercurial interprets this by looking at the directory name belaran@999: on the right hand side of the belaran@999: = sign; finding repositories belaran@999: in that directory hierarchy; and using the text on the belaran@999: left to strip off matching text from the belaran@999: names it will actually list in the web interface. The belaran@999: remaining component of a path after this stripping has belaran@999: occurred is called a virtual path. belaran@999: belaran@999: Given the example above, if we have a belaran@999: repository whose local path is /my/root/this/repo, the CGI belaran@999: script will strip the leading /my/root from the name, and belaran@999: publish the repository with a virtual path of this/repo. If the base URL for belaran@999: our CGI script is belaran@999: http://myhostname/~myuser/hgwebdir.cgi, the belaran@999: complete URL for that repository will be belaran@999: http://myhostname/~myuser/hgwebdir.cgi/this/repo. belaran@999: belaran@999: If we replace /my/root on the left hand side belaran@999: of this example with /my, then hgwebdir.cgi will only strip off belaran@999: /my from the repository belaran@999: name, and will give us a virtual path of root/this/repo instead of belaran@999: this/repo. belaran@999: belaran@999: The hgwebdir.cgi belaran@999: script will recursively search each directory listed in the belaran@999: collections section of its configuration belaran@999: file, but it will not recurse into the belaran@999: repositories it finds. belaran@999: belaran@999: The collections mechanism makes it easy belaran@999: to publish many repositories in a fire and belaran@999: forget manner. You only need to set up the CGI belaran@999: script and configuration file one time. Afterwards, you can belaran@999: publish or unpublish a repository at any time by simply moving belaran@999: it into, or out of, the directory hierarchy in which you've belaran@999: configured hgwebdir.cgi to belaran@999: look. belaran@999: belaran@999: belaran@999: Explicitly specifying which repositories to belaran@999: publish belaran@999: belaran@999: In addition to the collections belaran@999: mechanism, the hgwebdir.cgi script allows you belaran@999: to publish a specific list of repositories. To do so, belaran@999: create a paths section, with contents of belaran@999: the following form. belaran@999: [paths] belaran@999: repo1 = /my/path/to/some/repo belaran@999: repo2 = /some/path/to/another belaran@999: In this case, the virtual path (the component that will belaran@999: appear in a URL) is on the left hand side of each belaran@999: definition, while the path to the repository is on the belaran@999: right. Notice that there does not need to be any belaran@999: relationship between the virtual path you choose and the belaran@999: location of a repository in your filesystem. belaran@999: belaran@999: If you wish, you can use both the belaran@999: collections and paths belaran@999: mechanisms simultaneously in a single configuration belaran@999: file. belaran@999: belaran@999: belaran@999: Beware duplicate virtual paths belaran@999: belaran@999: If several repositories have the same belaran@999: virtual path, hgwebdir.cgi will not report belaran@999: an error. Instead, it will behave unpredictably. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Downloading source archives belaran@999: belaran@999: Mercurial's web interface lets users download an archive belaran@999: of any revision. This archive will contain a snapshot of the belaran@999: working directory as of that revision, but it will not contain belaran@999: a copy of the repository data. belaran@999: belaran@999: By default, this feature is not enabled. To enable it, belaran@999: you'll need to add an allow_archive item to the belaran@999: web section of your ~/.hgrc; see below for details. belaran@999: belaran@999: belaran@999: Web configuration options belaran@999: belaran@999: Mercurial's web interfaces (the hg belaran@999: serve command, and the hgweb.cgi and hgwebdir.cgi scripts) have a belaran@999: number of configuration options that you can set. These belaran@999: belong in a section named web. belaran@999: belaran@999: allow_archive: Determines belaran@999: which (if any) archive download mechanisms Mercurial belaran@999: supports. If you enable this feature, users of the web belaran@999: interface will be able to download an archive of whatever belaran@999: revision of a repository they are viewing. To enable the belaran@999: archive feature, this item must take the form of a belaran@999: sequence of words drawn from the list below. belaran@999: belaran@999: bz2: A belaran@999: tar archive, compressed using belaran@999: bzip2 compression. This has the belaran@999: best compression ratio, but uses the most CPU time on belaran@999: the server. belaran@999: belaran@999: gz: A belaran@999: tar archive, compressed using belaran@999: gzip compression. belaran@999: belaran@999: zip: A belaran@999: zip archive, compressed using LZW belaran@999: compression. This format has the worst compression belaran@999: ratio, but is widely used in the Windows world. belaran@999: belaran@999: belaran@999: If you provide an empty list, or don't have an belaran@999: allow_archive entry at belaran@999: all, this feature will be disabled. Here is an example of belaran@999: how to enable all three supported formats. belaran@999: [web] belaran@999: allow_archive = bz2 gz zip belaran@999: belaran@999: allowpull: belaran@999: Boolean. Determines whether the web interface allows belaran@999: remote users to hg pull belaran@999: and hg clone this belaran@999: repository over HTTP. If set to no or belaran@999: false, only the belaran@999: human-oriented portion of the web interface belaran@999: is available. belaran@999: belaran@999: contact: belaran@999: String. A free-form (but preferably brief) string belaran@999: identifying the person or group in charge of the belaran@999: repository. This often contains the name and email belaran@999: address of a person or mailing list. It often makes sense belaran@999: to place this entry in a repository's own .hg/hgrc file, but it can make belaran@999: sense to use in a global ~/.hgrc if every repository belaran@999: has a single maintainer. belaran@999: belaran@999: maxchanges: belaran@999: Integer. The default maximum number of changesets to belaran@999: display in a single page of output. belaran@999: belaran@999: maxfiles: belaran@999: Integer. The default maximum number of modified files to belaran@999: display in a single page of output. belaran@999: belaran@999: stripes: belaran@999: Integer. If the web interface displays alternating belaran@999: stripes to make it easier to visually align belaran@999: rows when you are looking at a table, this number controls belaran@999: the number of rows in each stripe. belaran@999: belaran@999: style: Controls the template belaran@999: Mercurial uses to display the web interface. Mercurial belaran@999: ships with several web templates. belaran@999: belaran@999: belaran@999: coal is monochromatic. belaran@999: belaran@999: belaran@999: gitweb emulates the visual belaran@999: style of git's web interface. belaran@999: belaran@999: belaran@999: monoblue uses solid blues and belaran@999: greys. belaran@999: belaran@999: belaran@999: paper is the default. belaran@999: belaran@999: belaran@999: spartan was the default for a belaran@999: long time. belaran@999: belaran@999: belaran@999: You can belaran@999: also specify a custom template of your own; see belaran@999: for details. Here, you can belaran@999: see how to enable the gitweb belaran@999: style. belaran@999: [web] belaran@999: style = gitweb belaran@999: belaran@999: templates: belaran@999: Path. The directory in which to search for template belaran@999: files. By default, Mercurial searches in the directory in belaran@999: which it was installed. belaran@999: belaran@999: If you are using hgwebdir.cgi, you can place a few belaran@999: configuration items in a web belaran@999: section of the hgweb.config file instead of a belaran@999: ~/.hgrc file, for belaran@999: convenience. These items are motd and style. belaran@999: belaran@999: belaran@999: Options specific to an individual repository belaran@999: belaran@999: A few web configuration belaran@999: items ought to be placed in a repository's local .hg/hgrc, rather than a user's belaran@999: or global ~/.hgrc. belaran@999: belaran@999: description: String. A belaran@999: free-form (but preferably brief) string that describes belaran@999: the contents or purpose of the repository. belaran@999: belaran@999: name: belaran@999: String. The name to use for the repository in the web belaran@999: interface. This overrides the default name, which is belaran@999: the last component of the repository's path. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Options specific to the <command role="hg-cmd" moreinfo="none">hg belaran@999: serve</command> command belaran@999: belaran@999: Some of the items in the web section of a ~/.hgrc file are only for use belaran@999: with the hg serve belaran@999: command. belaran@999: belaran@999: accesslog: belaran@999: Path. The name of a file into which to write an access belaran@999: log. By default, the hg belaran@999: serve command writes this information to belaran@999: standard output, not to a file. Log entries are written belaran@999: in the standard combined file format used belaran@999: by almost all web servers. belaran@999: belaran@999: address: belaran@999: String. The local address on which the server should belaran@999: listen for incoming connections. By default, the server belaran@999: listens on all addresses. belaran@999: belaran@999: errorlog: belaran@999: Path. The name of a file into which to write an error belaran@999: log. By default, the hg belaran@999: serve command writes this information to belaran@999: standard error, not to a file. belaran@999: belaran@999: ipv6: belaran@999: Boolean. Whether to use the IPv6 protocol. By default, belaran@999: IPv6 is not used. belaran@999: belaran@999: port: belaran@999: Integer. The TCP port number on which the server should belaran@999: listen. The default port number used is 8000. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Choosing the right <filename role="special" moreinfo="none">~/.hgrc</filename> file to add <literal role="rc-web" moreinfo="none">web</literal> items to belaran@999: belaran@999: It is important to remember that a web server like belaran@999: Apache or lighttpd will run under a user belaran@999: ID that is different to yours. CGI scripts run by your belaran@999: server, such as hgweb.cgi, will usually also run belaran@999: under that user ID. belaran@999: belaran@999: If you add web items to belaran@999: your own personal ~/.hgrc file, CGI scripts won't read that belaran@999: ~/.hgrc file. Those belaran@999: settings will thus only affect the behavior of the hg serve command when you run it. belaran@999: To cause CGI scripts to see your settings, either create a belaran@999: ~/.hgrc file in the belaran@999: home directory of the user ID that runs your web server, or belaran@999: add those settings to a system-wide hgrc file. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: System-wide configuration belaran@999: belaran@999: On Unix-like systems shared by multiple users (such as a belaran@999: server to which people publish changes), it often makes sense to belaran@999: set up some global default behaviors, such as what theme to use belaran@999: in web interfaces. belaran@999: belaran@999: If a file named /etc/mercurial/hgrc belaran@999: exists, Mercurial will read it at startup time and apply any belaran@999: configuration settings it finds in that file. It will also look belaran@999: for files ending in a .rc extension in a belaran@999: directory named /etc/mercurial/hgrc.d, and belaran@999: apply any configuration settings it finds in each of those belaran@999: files. belaran@999: belaran@999: belaran@999: Making Mercurial more trusting belaran@999: belaran@999: One situation in which a global hgrc belaran@999: can be useful is if users are pulling changes owned by other belaran@999: users. By default, Mercurial will not trust most of the belaran@999: configuration items in a .hg/hgrc file belaran@999: inside a repository that is owned by a different user. If we belaran@999: clone or pull changes from such a repository, Mercurial will belaran@999: print a warning stating that it does not trust their belaran@999: .hg/hgrc. belaran@999: belaran@999: If everyone in a particular Unix group is on the same team belaran@999: and should trust each other's belaran@999: configuration settings, or we want to trust particular users, belaran@999: we can override Mercurial's skeptical defaults by creating a belaran@999: system-wide hgrc file such as the belaran@999: following: belaran@999: belaran@999: # Save this as e.g. /etc/mercurial/hgrc.d/trust.rc belaran@999: [trusted] belaran@999: # Trust all entries in any hgrc file owned by the "editors" or belaran@999: # "www-data" groups. belaran@999: groups = editors, www-data belaran@999: belaran@999: # Trust entries in hgrc files owned by the following users. belaran@999: users = apache, bobo belaran@999: belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: File names and pattern matching belaran@999: belaran@999: Mercurial provides mechanisms that let you work with file belaran@999: names in a consistent and expressive way. belaran@999: belaran@999: belaran@999: Simple file naming belaran@999: belaran@999: Mercurial uses a unified piece of machinery under the belaran@999: hood to handle file names. Every command behaves belaran@999: uniformly with respect to file names. The way in which commands belaran@999: work with file names is as follows. belaran@999: belaran@999: If you explicitly name real files on the command line, belaran@999: Mercurial works with exactly those files, as you would expect. belaran@999: belaran@999: $ hg add COPYING README examples/simple.py belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: When you provide a directory name, Mercurial will interpret belaran@999: this as operate on every file in this directory and its belaran@999: subdirectories. Mercurial traverses the files and belaran@999: subdirectories in a directory in alphabetical order. When it belaran@999: encounters a subdirectory, it will traverse that subdirectory belaran@999: before continuing with the current directory. belaran@999: belaran@999: belaran@999: $ hg status src belaran@999: ? src/main.py belaran@999: ? src/watcher/_watcher.c belaran@999: ? src/watcher/watcher.py belaran@999: ? src/xyzzy.txt belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Running commands without any file names belaran@999: belaran@999: Mercurial's commands that work with file names have useful belaran@999: default behaviors when you invoke them without providing any belaran@999: file names or patterns. What kind of behavior you should belaran@999: expect depends on what the command does. Here are a few rules belaran@999: of thumb you can use to predict what a command is likely to do belaran@999: if you don't give it any names to work with. belaran@999: belaran@999: Most commands will operate on the entire working belaran@999: directory. This is what the hg belaran@999: add command does, for example. belaran@999: belaran@999: If the command has effects that are difficult or belaran@999: impossible to reverse, it will force you to explicitly belaran@999: provide at least one name or pattern (see below). This belaran@999: protects you from accidentally deleting files by running belaran@999: hg remove with no belaran@999: arguments, for example. belaran@999: belaran@999: belaran@999: It's easy to work around these default behaviors if they belaran@999: don't suit you. If a command normally operates on the whole belaran@999: working directory, you can invoke it on just the current belaran@999: directory and its subdirectories by giving it the name belaran@999: .. belaran@999: belaran@999: belaran@999: $ cd src belaran@999: $ hg add -n belaran@999: adding ../MANIFEST.in belaran@999: adding ../examples/performant.py belaran@999: adding ../setup.py belaran@999: adding main.py belaran@999: adding watcher/_watcher.c belaran@999: adding watcher/watcher.py belaran@999: adding xyzzy.txt belaran@999: $ hg add -n . belaran@999: adding main.py belaran@999: adding watcher/_watcher.c belaran@999: adding watcher/watcher.py belaran@999: adding xyzzy.txt belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Along the same lines, some commands normally print file belaran@999: names relative to the root of the repository, even if you're belaran@999: invoking them from a subdirectory. Such a command will print belaran@999: file names relative to your subdirectory if you give it explicit belaran@999: names. Here, we're going to run hg belaran@999: status from a subdirectory, and get it to operate on belaran@999: the entire working directory while printing file names relative belaran@999: to our subdirectory, by passing it the output of the hg root command. belaran@999: belaran@999: belaran@999: $ hg status belaran@999: A COPYING belaran@999: A README belaran@999: A examples/simple.py belaran@999: ? MANIFEST.in belaran@999: ? examples/performant.py belaran@999: ? setup.py belaran@999: ? src/main.py belaran@999: ? src/watcher/_watcher.c belaran@999: ? src/watcher/watcher.py belaran@999: ? src/xyzzy.txt belaran@999: $ hg status `hg root` belaran@999: A ../COPYING belaran@999: A ../README belaran@999: A ../examples/simple.py belaran@999: ? ../MANIFEST.in belaran@999: ? ../examples/performant.py belaran@999: ? ../setup.py belaran@999: ? main.py belaran@999: ? watcher/_watcher.c belaran@999: ? watcher/watcher.py belaran@999: ? xyzzy.txt belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Telling you what's going on belaran@999: belaran@999: The hg add example in the belaran@999: preceding section illustrates something else that's helpful belaran@999: about Mercurial commands. If a command operates on a file that belaran@999: you didn't name explicitly on the command line, it will usually belaran@999: print the name of the file, so that you will not be surprised belaran@999: what's going on. belaran@999: belaran@999: The principle here is of least belaran@999: surprise. If you've exactly named a file on the belaran@999: command line, there's no point in repeating it back at you. If belaran@999: Mercurial is acting on a file implicitly, e.g. belaran@999: because you provided no names, or a directory, or a pattern (see belaran@999: below), it is safest to tell you what files it's operating on. belaran@999: belaran@999: For commands that behave this way, you can silence them belaran@999: using the option. You belaran@999: can also get them to print the name of every file, even those belaran@999: you've named explicitly, using the option. belaran@999: belaran@999: belaran@999: belaran@999: Using patterns to identify files belaran@999: belaran@999: In addition to working with file and directory names, belaran@999: Mercurial lets you use patterns to identify belaran@999: files. Mercurial's pattern handling is expressive. belaran@999: belaran@999: On Unix-like systems (Linux, MacOS, etc.), the job of belaran@999: matching file names to patterns normally falls to the shell. On belaran@999: these systems, you must explicitly tell Mercurial that a name is belaran@999: a pattern. On Windows, the shell does not expand patterns, so belaran@999: Mercurial will automatically identify names that are patterns, belaran@999: and expand them for you. belaran@999: belaran@999: To provide a pattern in place of a regular name on the belaran@999: command line, the mechanism is simple: belaran@999: syntax:patternbody belaran@999: That is, a pattern is identified by a short text string that belaran@999: says what kind of pattern this is, followed by a colon, followed belaran@999: by the actual pattern. belaran@999: belaran@999: Mercurial supports two kinds of pattern syntax. The most belaran@999: frequently used is called glob; this is the belaran@999: same kind of pattern matching used by the Unix shell, and should belaran@999: be familiar to Windows command prompt users, too. belaran@999: belaran@999: When Mercurial does automatic pattern matching on Windows, belaran@999: it uses glob syntax. You can thus omit the belaran@999: glob: prefix on Windows, but belaran@999: it's safe to use it, too. belaran@999: belaran@999: The re syntax is more powerful; it lets belaran@999: you specify patterns using regular expressions, also known as belaran@999: regexps. belaran@999: belaran@999: By the way, in the examples that follow, notice that I'm belaran@999: careful to wrap all of my patterns in quote characters, so that belaran@999: they won't get expanded by the shell before Mercurial sees belaran@999: them. belaran@999: belaran@999: belaran@999: Shell-style <literal moreinfo="none">glob</literal> patterns belaran@999: belaran@999: This is an overview of the kinds of patterns you can use belaran@999: when you're matching on glob patterns. belaran@999: belaran@999: The * character matches belaran@999: any string, within a single directory. belaran@999: belaran@999: belaran@999: $ hg add 'glob:*.py' belaran@999: adding main.py belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The ** pattern matches belaran@999: any string, and crosses directory boundaries. It's not a belaran@999: standard Unix glob token, but it's accepted by several popular belaran@999: Unix shells, and is very useful. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg status 'glob:**.py' belaran@999: A examples/simple.py belaran@999: A src/main.py belaran@999: ? examples/performant.py belaran@999: ? setup.py belaran@999: ? src/watcher/watcher.py belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The ? pattern matches belaran@999: any single character. belaran@999: belaran@999: belaran@999: $ hg status 'glob:**.?' belaran@999: ? src/watcher/_watcher.c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The [ character begins a belaran@999: character class. This matches any single belaran@999: character within the class. The class ends with a belaran@999: ] character. A class may belaran@999: contain multiple ranges of the form belaran@999: a-f, which is shorthand for belaran@999: abcdef. belaran@999: belaran@999: belaran@999: $ hg status 'glob:**[nr-t]' belaran@999: ? MANIFEST.in belaran@999: ? src/xyzzy.txt belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If the first character after the belaran@999: [ in a character class is a belaran@999: !, it belaran@999: negates the class, making it match any belaran@999: single character not in the class. belaran@999: belaran@999: A { begins a group of belaran@999: subpatterns, where the whole group matches if any subpattern belaran@999: in the group matches. The , belaran@999: character separates subpatterns, and belaran@999: } ends the group. belaran@999: belaran@999: belaran@999: $ hg status 'glob:*.{in,py}' belaran@999: ? MANIFEST.in belaran@999: ? setup.py belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Watch out! belaran@999: belaran@999: Don't forget that if you want to match a pattern in any belaran@999: directory, you should not be using the belaran@999: * match-any token, as this belaran@999: will only match within one directory. Instead, use the belaran@999: ** token. This small belaran@999: example illustrates the difference between the two. belaran@999: belaran@999: belaran@999: $ hg status 'glob:*.py' belaran@999: ? setup.py belaran@999: $ hg status 'glob:**.py' belaran@999: A examples/simple.py belaran@999: A src/main.py belaran@999: ? examples/performant.py belaran@999: ? setup.py belaran@999: ? src/watcher/watcher.py belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Regular expression matching with <literal moreinfo="none">re</literal> belaran@999: patterns belaran@999: belaran@999: Mercurial accepts the same regular expression syntax as belaran@999: the Python programming language (it uses Python's regexp belaran@999: engine internally). This is based on the Perl language's belaran@999: regexp syntax, which is the most popular dialect in use (it's belaran@999: also used in Java, for example). belaran@999: belaran@999: I won't discuss Mercurial's regexp dialect in any detail belaran@999: here, as regexps are not often used. Perl-style regexps are belaran@999: in any case already exhaustively documented on a multitude of belaran@999: web sites, and in many books. Instead, I will focus here on a belaran@999: few things you should know if you find yourself needing to use belaran@999: regexps with Mercurial. belaran@999: belaran@999: A regexp is matched against an entire file name, relative belaran@999: to the root of the repository. In other words, even if you're belaran@999: already in subbdirectory foo, if you want to match files belaran@999: under this directory, your pattern must start with belaran@999: foo/. belaran@999: belaran@999: One thing to note, if you're familiar with Perl-style belaran@999: regexps, is that Mercurial's are rooted. belaran@999: That is, a regexp starts matching against the beginning of a belaran@999: string; it doesn't look for a match anywhere within the belaran@999: string. To match anywhere in a string, start your pattern belaran@999: with .*. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Filtering files belaran@999: belaran@999: Not only does Mercurial give you a variety of ways to belaran@999: specify files; it lets you further winnow those files using belaran@999: filters. Commands that work with file belaran@999: names accept two filtering options. belaran@999: belaran@999: , or belaran@999: , lets you belaran@999: specify a pattern that file names must match in order to be belaran@999: processed. belaran@999: belaran@999: , or belaran@999: , gives you a belaran@999: way to avoid processing files, if they belaran@999: match this pattern. belaran@999: belaran@999: You can provide multiple and options on the command line, belaran@999: and intermix them as you please. Mercurial interprets the belaran@999: patterns you provide using glob syntax by default (but you can belaran@999: use regexps if you need to). belaran@999: belaran@999: You can read a belaran@999: filter as process only the files that match this belaran@999: filter. belaran@999: belaran@999: belaran@999: $ hg status -I '*.in' belaran@999: ? MANIFEST.in belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The filter is best belaran@999: read as process only the files that don't match this belaran@999: pattern. belaran@999: belaran@999: belaran@999: $ hg status -X '**.py' src belaran@999: ? src/watcher/_watcher.c belaran@999: ? src/xyzzy.txt belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Permanently ignoring unwanted files and directories belaran@999: belaran@999: When you create a new repository, the chances are belaran@999: that over time it will grow to contain files that ought to belaran@999: not be managed by Mercurial, but which you belaran@999: don't want to see listed every time you run hg belaran@999: status. For instance, build products belaran@999: are files that are created as part of a build but which should belaran@999: not be managed by a revision control system. The most common belaran@999: build products are output files produced by software tools such belaran@999: as compilers. As another example, many text editors litter a belaran@999: directory with lock files, temporary working files, and backup belaran@999: files, which it also makes no sense to manage. belaran@999: belaran@999: To have Mercurial permanently ignore such files, create a belaran@999: file named .hgignore in the root of your belaran@999: repository. You should hg belaran@999: add this file so that it gets tracked with the rest of belaran@999: your repository contents, since your collaborators will probably belaran@999: find it useful too. belaran@999: belaran@999: By default, the .hgignore file should belaran@999: contain a list of regular expressions, one per line. Empty belaran@999: lines are skipped. Most people prefer to describe the files they belaran@999: want to ignore using the glob syntax that we belaran@999: described above, so a typical .hgignore belaran@999: file will start with this directive: belaran@999: belaran@999: syntax: glob belaran@999: belaran@999: This tells Mercurial to interpret the lines that follow as belaran@999: glob patterns, not regular expressions. belaran@999: belaran@999: Here is a typical-looking .hgignore belaran@999: file. belaran@999: belaran@999: syntax: glob belaran@999: # This line is a comment, and will be skipped. belaran@999: # Empty lines are skipped too. belaran@999: belaran@999: # Backup files left behind by the Emacs editor. belaran@999: *~ belaran@999: belaran@999: # Lock files used by the Emacs editor. belaran@999: # Notice that the "#" character is quoted with a backslash. belaran@999: # This prevents it from being interpreted as starting a comment. belaran@999: .\#* belaran@999: belaran@999: # Temporary files used by the vim editor. belaran@999: .*.swp belaran@999: belaran@999: # A hidden file created by the Mac OS X Finder. belaran@999: .DS_Store belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Case sensitivity belaran@999: belaran@999: If you're working in a mixed development environment that belaran@999: contains both Linux (or other Unix) systems and Macs or Windows belaran@999: systems, you should keep in the back of your mind the knowledge belaran@999: that they treat the case (N versus belaran@999: n) of file names in incompatible ways. This is belaran@999: not very likely to affect you, and it's easy to deal with if it belaran@999: does, but it could surprise you if you don't know about belaran@999: it. belaran@999: belaran@999: Operating systems and filesystems differ in the way they belaran@999: handle the case of characters in file and belaran@999: directory names. There are three common ways to handle case in belaran@999: names. belaran@999: belaran@999: Completely case insensitive. Uppercase and belaran@999: lowercase versions of a letter are treated as identical, belaran@999: both when creating a file and during subsequent accesses. belaran@999: This is common on older DOS-based systems. belaran@999: belaran@999: Case preserving, but insensitive. When a file belaran@999: or directory is created, the case of its name is stored, and belaran@999: can be retrieved and displayed by the operating system. belaran@999: When an existing file is being looked up, its case is belaran@999: ignored. This is the standard arrangement on Windows and belaran@999: MacOS. The names foo and belaran@999: FoO identify the same file. This belaran@999: treatment of uppercase and lowercase letters as belaran@999: interchangeable is also referred to as case belaran@999: folding. belaran@999: belaran@999: Case sensitive. The case of a name belaran@999: is significant at all times. The names belaran@999: foo and FoO belaran@999: identify different files. This is the way Linux and Unix belaran@999: systems normally work. belaran@999: belaran@999: belaran@999: On Unix-like systems, it is possible to have any or all of belaran@999: the above ways of handling case in action at once. For example, belaran@999: if you use a USB thumb drive formatted with a FAT32 filesystem belaran@999: on a Linux system, Linux will handle names on that filesystem in belaran@999: a case preserving, but insensitive, way. belaran@999: belaran@999: belaran@999: Safe, portable repository storage belaran@999: belaran@999: Mercurial's repository storage mechanism is case belaran@999: safe. It translates file names so that they can belaran@999: be safely stored on both case sensitive and case insensitive belaran@999: filesystems. This means that you can use normal file copying belaran@999: tools to transfer a Mercurial repository onto, for example, a belaran@999: USB thumb drive, and safely move that drive and repository belaran@999: back and forth between a Mac, a PC running Windows, and a belaran@999: Linux box. belaran@999: belaran@999: belaran@999: belaran@999: Detecting case conflicts belaran@999: belaran@999: When operating in the working directory, Mercurial honours belaran@999: the naming policy of the filesystem where the working belaran@999: directory is located. If the filesystem is case preserving, belaran@999: but insensitive, Mercurial will treat names that differ only belaran@999: in case as the same. belaran@999: belaran@999: An important aspect of this approach is that it is belaran@999: possible to commit a changeset on a case sensitive (typically belaran@999: Linux or Unix) filesystem that will cause trouble for users on belaran@999: case insensitive (usually Windows and MacOS) users. If a belaran@999: Linux user commits changes to two files, one named belaran@999: myfile.c and the other named belaran@999: MyFile.C, they will be stored correctly belaran@999: in the repository. And in the working directories of other belaran@999: Linux users, they will be correctly represented as separate belaran@999: files. belaran@999: belaran@999: If a Windows or Mac user pulls this change, they will not belaran@999: initially have a problem, because Mercurial's repository belaran@999: storage mechanism is case safe. However, once they try to belaran@999: hg update the working belaran@999: directory to that changeset, or hg belaran@999: merge with that changeset, Mercurial will spot the belaran@999: conflict between the two file names that the filesystem would belaran@999: treat as the same, and forbid the update or merge from belaran@999: occurring. belaran@999: belaran@999: belaran@999: belaran@999: Fixing a case conflict belaran@999: belaran@999: If you are using Windows or a Mac in a mixed environment belaran@999: where some of your collaborators are using Linux or Unix, and belaran@999: Mercurial reports a case folding conflict when you try to belaran@999: hg update or hg merge, the procedure to fix the belaran@999: problem is simple. belaran@999: belaran@999: Just find a nearby Linux or Unix box, clone the problem belaran@999: repository onto it, and use Mercurial's hg rename command to change the belaran@999: names of any offending files or directories so that they will belaran@999: no longer cause case folding conflicts. Commit this change, belaran@999: hg pull or hg push it across to your Windows or belaran@999: MacOS system, and hg update belaran@999: to the revision with the non-conflicting names. belaran@999: belaran@999: The changeset with case-conflicting names will remain in belaran@999: your project's history, and you still won't be able to belaran@999: hg update your working belaran@999: directory to that changeset on a Windows or MacOS system, but belaran@999: you can continue development unimpeded. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Managing releases and branchy development belaran@999: belaran@999: Mercurial provides several mechanisms for you to manage a belaran@999: project that is making progress on multiple fronts at once. To belaran@999: understand these mechanisms, let's first take a brief look at a belaran@999: fairly normal software project structure. belaran@999: belaran@999: Many software projects issue periodic major belaran@999: releases that contain substantial new features. In parallel, they belaran@999: may issue minor releases. These are usually belaran@999: identical to the major releases off which they're based, but with belaran@999: a few bugs fixed. belaran@999: belaran@999: In this chapter, we'll start by talking about how to keep belaran@999: records of project milestones such as releases. We'll then belaran@999: continue on to talk about the flow of work between different belaran@999: phases of a project, and how Mercurial can help you to isolate and belaran@999: manage this work. belaran@999: belaran@999: belaran@999: Giving a persistent name to a revision belaran@999: belaran@999: Once you decide that you'd like to call a particular belaran@999: revision a release, it's a good idea to record belaran@999: the identity of that revision. This will let you reproduce that belaran@999: release at a later date, for whatever purpose you might need at belaran@999: the time (reproducing a bug, porting to a new platform, etc). belaran@999: belaran@999: $ hg init mytag belaran@999: $ cd mytag belaran@999: $ echo hello > myfile belaran@999: $ hg commit -A -m 'Initial commit' belaran@999: adding myfile belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial lets you give a permanent name to any revision belaran@999: using the hg tag command. Not belaran@999: surprisingly, these names are called tags. belaran@999: belaran@999: belaran@999: $ hg tag v1.0 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: A tag is nothing more than a symbolic name belaran@999: for a revision. Tags exist purely for your convenience, so that belaran@999: you have a handy permanent way to refer to a revision; Mercurial belaran@999: doesn't interpret the tag names you use in any way. Neither belaran@999: does Mercurial place any restrictions on the name of a tag, belaran@999: beyond a few that are necessary to ensure that a tag can be belaran@999: parsed unambiguously. A tag name cannot contain any of the belaran@999: following characters: belaran@999: belaran@999: Colon (ASCII 58, belaran@999: :) belaran@999: belaran@999: Carriage return (ASCII 13, belaran@999: \r) belaran@999: belaran@999: Newline (ASCII 10, belaran@999: \n) belaran@999: belaran@999: belaran@999: You can use the hg tags belaran@999: command to display the tags present in your repository. In the belaran@999: output, each tagged revision is identified first by its name, belaran@999: then by revision number, and finally by the unique hash of the belaran@999: revision. belaran@999: belaran@999: belaran@999: $ hg tags belaran@999: tip 1:f283c2669b38 belaran@999: v1.0 0:0c957785065f belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Notice that tip is listed in the output belaran@999: of hg tags. The belaran@999: tip tag is a special floating belaran@999: tag, which always identifies the newest revision in the belaran@999: repository. belaran@999: belaran@999: In the output of the hg belaran@999: tags command, tags are listed in reverse order, by belaran@999: revision number. This usually means that recent tags are listed belaran@999: before older tags. It also means that tip is belaran@999: always going to be the first tag listed in the output of belaran@999: hg tags. belaran@999: belaran@999: When you run hg log, if it belaran@999: displays a revision that has tags associated with it, it will belaran@999: print those tags. belaran@999: belaran@999: belaran@999: $ hg log belaran@999: changeset: 1:f283c2669b38 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:16 2009 +0000 belaran@999: summary: Added tag v1.0 for changeset 0c957785065f belaran@999: belaran@999: changeset: 0:0c957785065f belaran@999: tag: v1.0 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:15 2009 +0000 belaran@999: summary: Initial commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Any time you need to provide a revision ID to a Mercurial belaran@999: command, the command will accept a tag name in its place. belaran@999: Internally, Mercurial will translate your tag name into the belaran@999: corresponding revision ID, then use that. belaran@999: belaran@999: belaran@999: $ echo goodbye > myfile2 belaran@999: $ hg commit -A -m 'Second commit' belaran@999: adding myfile2 belaran@999: $ hg log -r v1.0 belaran@999: changeset: 0:0c957785065f belaran@999: tag: v1.0 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:15 2009 +0000 belaran@999: summary: Initial commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: There's no limit on the number of tags you can have in a belaran@999: repository, or on the number of tags that a single revision can belaran@999: have. As a practical matter, it's not a great idea to have belaran@999: too many (a number which will vary from project belaran@999: to project), simply because tags are supposed to help you to belaran@999: find revisions. If you have lots of tags, the ease of using belaran@999: them to identify revisions diminishes rapidly. belaran@999: belaran@999: For example, if your project has milestones as frequent as belaran@999: every few days, it's perfectly reasonable to tag each one of belaran@999: those. But if you have a continuous build system that makes belaran@999: sure every revision can be built cleanly, you'd be introducing a belaran@999: lot of noise if you were to tag every clean build. Instead, you belaran@999: could tag failed builds (on the assumption that they're rare!), belaran@999: or simply not use tags to track buildability. belaran@999: belaran@999: If you want to remove a tag that you no longer want, use belaran@999: hg tag --remove. belaran@999: belaran@999: belaran@999: $ hg tag --remove v1.0 belaran@999: $ hg tags belaran@999: tip 3:0f446f1d1f7f belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You can also modify a tag at any time, so that it identifies belaran@999: a different revision, by simply issuing a new hg tag command. You'll have to use the belaran@999: option to tell Mercurial belaran@999: that you really want to update the belaran@999: tag. belaran@999: belaran@999: belaran@999: $ hg tag -r 1 v1.1 belaran@999: $ hg tags belaran@999: tip 4:12fc7bf92915 belaran@999: v1.1 1:f283c2669b38 belaran@999: $ hg tag -r 2 v1.1 belaran@999: abort: tag 'v1.1' already exists (use -f to force) belaran@999: $ hg tag -f -r 2 v1.1 belaran@999: $ hg tags belaran@999: tip 5:17e25cf010af belaran@999: v1.1 2:737882b3cc76 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: There will still be a permanent record of the previous belaran@999: identity of the tag, but Mercurial will no longer use it. belaran@999: There's thus no penalty to tagging the wrong revision; all you belaran@999: have to do is turn around and tag the correct revision once you belaran@999: discover your error. belaran@999: belaran@999: Mercurial stores tags in a normal revision-controlled file belaran@999: in your repository. If you've created any tags, you'll find belaran@999: them in a file in the root of your repository named .hgtags. When you run the hg tag command, Mercurial modifies belaran@999: this file, then automatically commits the change to it. This belaran@999: means that every time you run hg belaran@999: tag, you'll see a corresponding changeset in the belaran@999: output of hg log. belaran@999: belaran@999: belaran@999: $ hg tip belaran@999: changeset: 5:17e25cf010af belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:16 2009 +0000 belaran@999: summary: Added tag v1.1 for changeset 737882b3cc76 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Handling tag conflicts during a merge belaran@999: belaran@999: You won't often need to care about the .hgtags file, but it sometimes belaran@999: makes its presence known during a merge. The format of the belaran@999: file is simple: it consists of a series of lines. Each line belaran@999: starts with a changeset hash, followed by a space, followed by belaran@999: the name of a tag. belaran@999: belaran@999: If you're resolving a conflict in the .hgtags file during a merge, belaran@999: there's one twist to modifying the .hgtags file: when Mercurial is belaran@999: parsing the tags in a repository, it belaran@999: never reads the working copy of the belaran@999: .hgtags file. Instead, it belaran@999: reads the most recently committed belaran@999: revision of the file. belaran@999: belaran@999: An unfortunate consequence of this design is that you belaran@999: can't actually verify that your merged .hgtags file is correct until belaran@999: after you've committed a change. So if belaran@999: you find yourself resolving a conflict on .hgtags during a merge, be sure to belaran@999: run hg tags after you commit. belaran@999: If it finds an error in the .hgtags file, it will report the belaran@999: location of the error, which you can then fix and commit. You belaran@999: should then run hg tags belaran@999: again, just to be sure that your fix is correct. belaran@999: belaran@999: belaran@999: belaran@999: Tags and cloning belaran@999: belaran@999: You may have noticed that the hg belaran@999: clone command has a option that lets you clone belaran@999: an exact copy of the repository as of a particular changeset. belaran@999: The new clone will not contain any project history that comes belaran@999: after the revision you specified. This has an interaction belaran@999: with tags that can surprise the unwary. belaran@999: belaran@999: Recall that a tag is stored as a revision to belaran@999: the .hgtags file. When you belaran@999: create a tag, the changeset in which its recorded refers to an belaran@999: older changeset. When you run hg clone belaran@999: -r foo to clone a repository as of tag belaran@999: foo, the new clone will not belaran@999: contain any revision newer than the one the tag refers to, belaran@999: including the revision where the tag was created. belaran@999: The result is that you'll get exactly the right subset of the belaran@999: project's history in the new repository, but belaran@999: not the tag you might have belaran@999: expected. belaran@999: belaran@999: belaran@999: belaran@999: When permanent tags are too much belaran@999: belaran@999: Since Mercurial's tags are revision controlled and carried belaran@999: around with a project's history, everyone you work with will belaran@999: see the tags you create. But giving names to revisions has belaran@999: uses beyond simply noting that revision belaran@999: 4237e45506ee is really belaran@999: v2.0.2. If you're trying to track down a belaran@999: subtle bug, you might want a tag to remind you of something belaran@999: like Anne saw the symptoms with this belaran@999: revision. belaran@999: belaran@999: For cases like this, what you might want to use are belaran@999: local tags. You can create a local tag belaran@999: with the option to the belaran@999: hg tag command. This will belaran@999: store the tag in a file called .hg/localtags. Unlike .hgtags, .hg/localtags is not revision belaran@999: controlled. Any tags you create using remain strictly local to the belaran@999: repository you're currently working in. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The flow of changes—big picture vs. little belaran@999: belaran@999: To return to the outline I sketched at the belaran@999: beginning of the chapter, let's think about a project that has belaran@999: multiple concurrent pieces of work under development at belaran@999: once. belaran@999: belaran@999: There might be a push for a new main release; belaran@999: a new minor bugfix release to the last main release; and an belaran@999: unexpected hot fix to an old release that is now belaran@999: in maintenance mode. belaran@999: belaran@999: The usual way people refer to these different concurrent belaran@999: directions of development is as branches. belaran@999: However, we've already seen numerous times that Mercurial treats belaran@999: all of history as a series of branches and belaran@999: merges. Really, what we have here is two ideas that are belaran@999: peripherally related, but which happen to share a name. belaran@999: belaran@999: Big picture branches represent belaran@999: the sweep of a project's evolution; people give them names, belaran@999: and talk about them in conversation. belaran@999: belaran@999: Little picture branches are belaran@999: artefacts of the day-to-day activity of developing and belaran@999: merging changes. They expose the narrative of how the code belaran@999: was developed. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Managing big-picture branches in repositories belaran@999: belaran@999: The easiest way to isolate a big picture belaran@999: branch in Mercurial is in a dedicated repository. If you have belaran@999: an existing shared repository—let's call it belaran@999: myproject—that reaches a belaran@999: 1.0 milestone, you can start to prepare for belaran@999: future maintenance releases on top of version 1.0 by tagging the belaran@999: revision from which you prepared the 1.0 release. belaran@999: belaran@999: belaran@999: $ cd myproject belaran@999: $ hg tag v1.0 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You can then clone a new shared belaran@999: myproject-1.0.1 repository as of that belaran@999: tag. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone myproject myproject-1.0.1 belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Afterwards, if someone needs to work on a bug fix that ought belaran@999: to go into an upcoming 1.0.1 minor release, they clone the belaran@999: myproject-1.0.1 repository, make their belaran@999: changes, and push them back. belaran@999: belaran@999: belaran@999: $ hg clone myproject-1.0.1 my-1.0.1-bugfix belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd my-1.0.1-bugfix belaran@999: $ echo 'I fixed a bug using only echo!' >> myfile belaran@999: $ hg commit -m 'Important fix for 1.0.1' belaran@999: $ hg push belaran@999: pushing to /tmp/branch-repo3rVLLS/myproject-1.0.1 belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Meanwhile, development for belaran@999: the next major release can continue, isolated and unabated, in belaran@999: the myproject repository. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone myproject my-feature belaran@999: updating working directory belaran@999: 2 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd my-feature belaran@999: $ echo 'This sure is an exciting new feature!' > mynewfile belaran@999: $ hg commit -A -m 'New feature' belaran@999: adding mynewfile belaran@999: $ hg push belaran@999: pushing to /tmp/branch-repo3rVLLS/myproject belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Don't repeat yourself: merging across branches belaran@999: belaran@999: In many cases, if you have a bug to fix on a maintenance belaran@999: branch, the chances are good that the bug exists on your belaran@999: project's main branch (and possibly other maintenance branches, belaran@999: too). It's a rare developer who wants to fix the same bug belaran@999: multiple times, so let's look at a few ways that Mercurial can belaran@999: help you to manage these bugfixes without duplicating your belaran@999: work. belaran@999: belaran@999: In the simplest instance, all you need to do is pull changes belaran@999: from your maintenance branch into your local clone of the target belaran@999: branch. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone myproject myproject-merge belaran@999: updating working directory belaran@999: 3 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd myproject-merge belaran@999: $ hg pull ../myproject-1.0.1 belaran@999: pulling from ../myproject-1.0.1 belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 1 changes to 1 files (+1 heads) belaran@999: (run 'hg heads' to see heads, 'hg merge' to merge) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You'll then need to merge the heads of the two branches, and belaran@999: push back to the main branch. belaran@999: belaran@999: belaran@999: $ hg merge belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: (branch merge, don't forget to commit) belaran@999: $ hg commit -m 'Merge bugfix from 1.0.1 branch' belaran@999: $ hg push belaran@999: pushing to /tmp/branch-repo3rVLLS/myproject belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 2 changesets with 1 changes to 1 files belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Naming branches within one repository belaran@999: belaran@999: In most instances, isolating branches in repositories is the belaran@999: right approach. Its simplicity makes it easy to understand; and belaran@999: so it's hard to make mistakes. There's a one-to-one belaran@999: relationship between branches you're working in and directories belaran@999: on your system. This lets you use normal (non-Mercurial-aware) belaran@999: tools to work on files within a branch/repository. belaran@999: belaran@999: If you're more in the power user category belaran@999: (and your collaborators are too), there is belaran@999: an alternative way of handling branches that you can consider. belaran@999: I've already mentioned the human-level distinction between belaran@999: small picture and big picture belaran@999: branches. While Mercurial works with multiple small belaran@999: picture branches in a repository all the time (for belaran@999: example after you pull changes in, but before you merge them), belaran@999: it can also work with multiple big belaran@999: picture branches. belaran@999: belaran@999: The key to working this way is that Mercurial lets you belaran@999: assign a persistent name to a branch. belaran@999: There always exists a branch named default. belaran@999: Even before you start naming branches yourself, you can find belaran@999: traces of the default branch if you look for belaran@999: them. belaran@999: belaran@999: As an example, when you run the hg belaran@999: commit command, and it pops up your editor so that belaran@999: you can enter a commit message, look for a line that contains belaran@999: the text HG: branch default at belaran@999: the bottom. This is telling you that your commit will occur on belaran@999: the branch named default. belaran@999: belaran@999: To start working with named branches, use the hg branches command. This command belaran@999: lists the named branches already present in your repository, belaran@999: telling you which changeset is the tip of each. belaran@999: belaran@999: belaran@999: $ hg tip belaran@999: changeset: 0:90897f9e54e3 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Initial commit belaran@999: belaran@999: $ hg branches belaran@999: default 0:90897f9e54e3 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Since you haven't created any named branches yet, the only belaran@999: one that exists is default. belaran@999: belaran@999: To find out what the current branch is, run belaran@999: the hg branch command, giving belaran@999: it no arguments. This tells you what branch the parent of the belaran@999: current changeset is on. belaran@999: belaran@999: belaran@999: $ hg branch belaran@999: default belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: To create a new branch, run the hg belaran@999: branch command again. This time, give it one belaran@999: argument: the name of the branch you want to create. belaran@999: belaran@999: belaran@999: $ hg branch foo belaran@999: marked working directory as branch foo belaran@999: $ hg branch belaran@999: foo belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: After you've created a branch, you might wonder what effect belaran@999: the hg branch command has had. belaran@999: What do the hg status and belaran@999: hg tip commands report? belaran@999: belaran@999: belaran@999: $ hg status belaran@999: $ hg tip belaran@999: changeset: 0:90897f9e54e3 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Initial commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Nothing has changed in the belaran@999: working directory, and there's been no new history created. As belaran@999: this suggests, running the hg belaran@999: branch command has no permanent effect; it only belaran@999: tells Mercurial what branch name to use the belaran@999: next time you commit a changeset. belaran@999: belaran@999: When you commit a change, Mercurial records the name of the belaran@999: branch on which you committed. Once you've switched from the belaran@999: default branch to another and committed, belaran@999: you'll see the name of the new branch show up in the output of belaran@999: hg log, hg tip, and other commands that belaran@999: display the same kind of output. belaran@999: belaran@999: belaran@999: $ echo 'hello again' >> myfile belaran@999: $ hg commit -m 'Second commit' belaran@999: $ hg tip belaran@999: changeset: 1:5656f8ffdd49 belaran@999: branch: foo belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Second commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The hg log-like commands belaran@999: will print the branch name of every changeset that's not on the belaran@999: default branch. As a result, if you never belaran@999: use named branches, you'll never see this information. belaran@999: belaran@999: Once you've named a branch and committed a change with that belaran@999: name, every subsequent commit that descends from that change belaran@999: will inherit the same branch name. You can change the name of a belaran@999: branch at any time, using the hg belaran@999: branch command. belaran@999: belaran@999: belaran@999: $ hg branch belaran@999: foo belaran@999: $ hg branch bar belaran@999: marked working directory as branch bar belaran@999: $ echo new file > newfile belaran@999: $ hg commit -A -m 'Third commit' belaran@999: adding newfile belaran@999: $ hg tip belaran@999: changeset: 2:c59d680fc2ec belaran@999: branch: bar belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Third commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: In practice, this is something you won't do very often, as belaran@999: branch names tend to have fairly long lifetimes. (This isn't a belaran@999: rule, just an observation.) belaran@999: belaran@999: belaran@999: belaran@999: Dealing with multiple named branches in a belaran@999: repository belaran@999: belaran@999: If you have more than one named branch in a repository, belaran@999: Mercurial will remember the branch that your working directory belaran@999: is on when you start a command like hg belaran@999: update or hg pull belaran@999: -u. It will update the working directory to the tip belaran@999: of this branch, no matter what the repo-wide tip belaran@999: is. To update to a revision that's on a different named branch, belaran@999: you may need to use the belaran@999: option to hg update. belaran@999: belaran@999: This behavior is a little subtle, so let's see it in belaran@999: action. First, let's remind ourselves what branch we're belaran@999: currently on, and what branches are in our repository. belaran@999: belaran@999: belaran@999: $ hg parents belaran@999: changeset: 2:c59d680fc2ec belaran@999: branch: bar belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Third commit belaran@999: belaran@999: $ hg branches belaran@999: bar 2:c59d680fc2ec belaran@999: foo 1:5656f8ffdd49 (inactive) belaran@999: default 0:90897f9e54e3 (inactive) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: We're on the bar branch, but there also belaran@999: exists an older hg foo belaran@999: branch. belaran@999: belaran@999: We can hg update back and belaran@999: forth between the tips of the foo and belaran@999: bar branches without needing to use the belaran@999: option, because this belaran@999: only involves going backwards and forwards linearly through our belaran@999: change history. belaran@999: belaran@999: belaran@999: $ hg update foo belaran@999: 0 files updated, 0 files merged, 1 files removed, 0 files unresolved belaran@999: $ hg parents belaran@999: changeset: 1:5656f8ffdd49 belaran@999: branch: foo belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Second commit belaran@999: belaran@999: $ hg update bar belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ hg parents belaran@999: changeset: 2:c59d680fc2ec belaran@999: branch: bar belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Third commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If we go back to the foo branch and then belaran@999: run hg update, it will keep us belaran@999: on foo, not move us to the tip of belaran@999: bar. belaran@999: belaran@999: belaran@999: $ hg update foo belaran@999: 0 files updated, 0 files merged, 1 files removed, 0 files unresolved belaran@999: $ hg update belaran@999: 0 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Committing a new change on the foo branch belaran@999: introduces a new head. belaran@999: belaran@999: belaran@999: $ echo something > somefile belaran@999: $ hg commit -A -m 'New file' belaran@999: adding somefile belaran@999: created new head belaran@999: $ hg heads belaran@999: changeset: 3:4dd2f7a37288 belaran@999: branch: foo belaran@999: tag: tip belaran@999: parent: 1:5656f8ffdd49 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:43 2009 +0000 belaran@999: summary: New file belaran@999: belaran@999: changeset: 2:c59d680fc2ec belaran@999: branch: bar belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:42 2009 +0000 belaran@999: summary: Third commit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Branch names and merging belaran@999: belaran@999: As you've probably noticed, merges in Mercurial are not belaran@999: symmetrical. Let's say our repository has two heads, 17 and 23. belaran@999: If I hg update to 17 and then belaran@999: hg merge with 23, Mercurial belaran@999: records 17 as the first parent of the merge, and 23 as the belaran@999: second. Whereas if I hg update belaran@999: to 23 and then hg merge with belaran@999: 17, it records 23 as the first parent, and 17 as the belaran@999: second. belaran@999: belaran@999: This affects Mercurial's choice of branch name when you belaran@999: merge. After a merge, Mercurial will retain the branch name of belaran@999: the first parent when you commit the result of the merge. If belaran@999: your first parent's branch name is foo, and belaran@999: you merge with bar, the branch name will belaran@999: still be foo after you merge. belaran@999: belaran@999: It's not unusual for a repository to contain multiple heads, belaran@999: each with the same branch name. Let's say I'm working on the belaran@999: foo branch, and so are you. We commit belaran@999: different changes; I pull your changes; I now have two heads, belaran@999: each claiming to be on the foo branch. The belaran@999: result of a merge will be a single head on the belaran@999: foo branch, as you might hope. belaran@999: belaran@999: But if I'm working on the bar branch, and belaran@999: I merge work from the foo branch, the result belaran@999: will remain on the bar branch. belaran@999: belaran@999: belaran@999: $ hg branch belaran@999: bar belaran@999: $ hg merge foo belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: (branch merge, don't forget to commit) belaran@999: $ hg commit -m 'Merge' belaran@999: $ hg tip belaran@999: changeset: 4:9f05d4ef3efe belaran@999: branch: bar belaran@999: tag: tip belaran@999: parent: 2:c59d680fc2ec belaran@999: parent: 3:4dd2f7a37288 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:44 2009 +0000 belaran@999: summary: Merge belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: To give a more concrete example, if I'm working on the belaran@999: bleeding-edge branch, and I want to bring in belaran@999: the latest fixes from the stable branch, belaran@999: Mercurial will choose the right belaran@999: (bleeding-edge) branch name when I pull and belaran@999: merge from stable. belaran@999: belaran@999: belaran@999: belaran@999: Branch naming is generally useful belaran@999: belaran@999: You shouldn't think of named branches as applicable only to belaran@999: situations where you have multiple long-lived branches belaran@999: cohabiting in a single repository. They're very useful even in belaran@999: the one-branch-per-repository case. belaran@999: belaran@999: In the simplest case, giving a name to each branch gives you belaran@999: a permanent record of which branch a changeset originated on. belaran@999: This gives you more context when you're trying to follow the belaran@999: history of a long-lived branchy project. belaran@999: belaran@999: If you're working with shared repositories, you can set up a belaran@999: pretxnchangegroup hook on each belaran@999: that will block incoming changes that have the belaran@999: wrong branch name. This provides a simple, but belaran@999: effective, defence against people accidentally pushing changes belaran@999: from a bleeding edge branch to a belaran@999: stable branch. Such a hook might look like this belaran@999: inside the shared repo's belaran@999: /.hgrc. belaran@999: [hooks] belaran@999: pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Finding and fixing mistakes belaran@999: belaran@999: To err might be human, but to really handle the consequences belaran@999: well takes a top-notch revision control system. In this chapter, belaran@999: we'll discuss some of the techniques you can use when you find belaran@999: that a problem has crept into your project. Mercurial has some belaran@999: highly capable features that will help you to isolate the sources belaran@999: of problems, and to handle them appropriately. belaran@999: belaran@999: belaran@999: Erasing local history belaran@999: belaran@999: belaran@999: The accidental commit belaran@999: belaran@999: I have the occasional but persistent problem of typing belaran@999: rather more quickly than I can think, which sometimes results belaran@999: in me committing a changeset that is either incomplete or belaran@999: plain wrong. In my case, the usual kind of incomplete belaran@999: changeset is one in which I've created a new source file, but belaran@999: forgotten to hg add it. A belaran@999: plain wrong changeset is not as common, but no belaran@999: less annoying. belaran@999: belaran@999: belaran@999: belaran@999: Rolling back a transaction belaran@999: belaran@999: In , I belaran@999: mentioned that Mercurial treats each modification of a belaran@999: repository as a transaction. Every time belaran@999: you commit a changeset or pull changes from another belaran@999: repository, Mercurial remembers what you did. You can undo, belaran@999: or roll back, exactly one of these belaran@999: actions using the hg rollback belaran@999: command. (See belaran@999: for an important caveat about the use of this command.) belaran@999: belaran@999: Here's a mistake that I often find myself making: belaran@999: committing a change in which I've created a new file, but belaran@999: forgotten to hg add belaran@999: it. belaran@999: belaran@999: belaran@999: $ hg status belaran@999: M a belaran@999: $ echo b > b belaran@999: $ hg commit -m 'Add file b' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Looking at the output of hg belaran@999: status after the commit immediately confirms the belaran@999: error. belaran@999: belaran@999: belaran@999: $ hg status belaran@999: ? b belaran@999: $ hg tip belaran@999: changeset: 1:246e2aada1c5 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:14 2009 +0000 belaran@999: summary: Add file b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The commit captured the changes to the file belaran@999: a, but not the new file belaran@999: b. If I were to push this changeset to a belaran@999: repository that I shared with a colleague, the chances are belaran@999: high that something in a would refer to belaran@999: b, which would not be present in their belaran@999: repository when they pulled my changes. I would thus become belaran@999: the object of some indignation. belaran@999: belaran@999: However, luck is with me—I've caught my error belaran@999: before I pushed the changeset. I use the hg rollback command, and Mercurial belaran@999: makes that last changeset vanish. belaran@999: belaran@999: belaran@999: $ hg rollback belaran@999: rolling back last transaction belaran@999: $ hg tip belaran@999: changeset: 0:c37ce4157509 belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:14 2009 +0000 belaran@999: summary: First commit belaran@999: belaran@999: $ hg status belaran@999: M a belaran@999: ? b belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Notice that the changeset is no longer present in the belaran@999: repository's history, and the working directory once again belaran@999: thinks that the file a is modified. The belaran@999: commit and rollback have left the working directory exactly as belaran@999: it was prior to the commit; the changeset has been completely belaran@999: erased. I can now safely hg belaran@999: add the file b, and rerun my belaran@999: commit. belaran@999: belaran@999: belaran@999: $ hg add b belaran@999: $ hg commit -m 'Add file b, this time for real' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The erroneous pull belaran@999: belaran@999: It's common practice with Mercurial to maintain separate belaran@999: development branches of a project in different repositories. belaran@999: Your development team might have one shared repository for belaran@999: your project's 0.9 release, and another, belaran@999: containing different changes, for the 1.0 belaran@999: release. belaran@999: belaran@999: Given this, you can imagine that the consequences could be belaran@999: messy if you had a local 0.9 repository, and belaran@999: accidentally pulled changes from the shared 1.0 belaran@999: repository into it. At worst, you could be paying belaran@999: insufficient attention, and push those changes into the shared belaran@999: 0.9 tree, confusing your entire team (but don't belaran@999: worry, we'll return to this horror scenario later). However, belaran@999: it's more likely that you'll notice immediately, because belaran@999: Mercurial will display the URL it's pulling from, or you will belaran@999: see it pull a suspiciously large number of changes into the belaran@999: repository. belaran@999: belaran@999: The hg rollback command belaran@999: will work nicely to expunge all of the changesets that you belaran@999: just pulled. Mercurial groups all changes from one hg pull into a single transaction, belaran@999: so one hg rollback is all you belaran@999: need to undo this mistake. belaran@999: belaran@999: belaran@999: belaran@999: Rolling back is useless once you've pushed belaran@999: belaran@999: The value of the hg belaran@999: rollback command drops to zero once you've pushed belaran@999: your changes to another repository. Rolling back a change belaran@999: makes it disappear entirely, but only in belaran@999: the repository in which you perform the hg rollback. Because a rollback belaran@999: eliminates history, there's no way for the disappearance of a belaran@999: change to propagate between repositories. belaran@999: belaran@999: If you've pushed a change to another belaran@999: repository—particularly if it's a shared belaran@999: repository—it has essentially escaped into the belaran@999: wild, and you'll have to recover from your mistake belaran@999: in a different way. If you push a changeset somewhere, then belaran@999: roll it back, then pull from the repository you pushed to, the belaran@999: changeset you thought you'd gotten rid of will simply reappear belaran@999: in your repository. belaran@999: belaran@999: (If you absolutely know for sure that the change belaran@999: you want to roll back is the most recent change in the belaran@999: repository that you pushed to, and you belaran@999: know that nobody else could have pulled it from that belaran@999: repository, you can roll back the changeset there, too, but belaran@999: you really should not expect this to work reliably. Sooner or belaran@999: later a change really will make it into a repository that you belaran@999: don't directly control (or have forgotten about), and come belaran@999: back to bite you.) belaran@999: belaran@999: belaran@999: belaran@999: You can only roll back once belaran@999: belaran@999: Mercurial stores exactly one transaction in its belaran@999: transaction log; that transaction is the most recent one that belaran@999: occurred in the repository. This means that you can only roll belaran@999: back one transaction. If you expect to be able to roll back belaran@999: one transaction, then its predecessor, this is not the belaran@999: behavior you will get. belaran@999: belaran@999: belaran@999: $ hg rollback belaran@999: rolling back last transaction belaran@999: $ hg rollback belaran@999: no rollback information available belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Once you've rolled back one transaction in a repository, belaran@999: you can't roll back again in that repository until you perform belaran@999: another commit or pull. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Reverting the mistaken change belaran@999: belaran@999: If you make a modification to a file, and decide that you belaran@999: really didn't want to change the file at all, and you haven't belaran@999: yet committed your changes, the hg belaran@999: revert command is the one you'll need. It looks at belaran@999: the changeset that's the parent of the working directory, and belaran@999: restores the contents of the file to their state as of that belaran@999: changeset. (That's a long-winded way of saying that, in the belaran@999: normal case, it undoes your modifications.) belaran@999: belaran@999: Let's illustrate how the hg belaran@999: revert command works with yet another small example. belaran@999: We'll begin by modifying a file that Mercurial is already belaran@999: tracking. belaran@999: belaran@999: belaran@999: $ cat file belaran@999: original content belaran@999: $ echo unwanted change >> file belaran@999: $ hg diff file belaran@999: diff -r 2eacf948d309 file belaran@999: --- a/file Sun Aug 16 14:05:00 2009 +0000 belaran@999: +++ b/file Sun Aug 16 14:05:00 2009 +0000 belaran@999: @@ -1,1 +1,2 @@ belaran@999: original content belaran@999: +unwanted change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If we don't belaran@999: want that change, we can simply hg belaran@999: revert the file. belaran@999: belaran@999: belaran@999: $ hg status belaran@999: M file belaran@999: $ hg revert file belaran@999: $ cat file belaran@999: original content belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The hg revert command belaran@999: provides us with an extra degree of safety by saving our belaran@999: modified file with a .orig belaran@999: extension. belaran@999: belaran@999: belaran@999: $ hg status belaran@999: ? file.orig belaran@999: $ cat file.orig belaran@999: original content belaran@999: unwanted change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Be careful with <filename moreinfo="none">.orig</filename> files belaran@999: belaran@999: It's extremely unlikely that you are either using belaran@999: Mercurial to manage files with .orig belaran@999: extensions or that you even care about the contents of such belaran@999: files. Just in case, though, it's useful to remember that belaran@999: hg revert will belaran@999: unconditionally overwrite an existing file with a belaran@999: .orig extension. For instance, if you belaran@999: already have a file named foo.orig when belaran@999: you revert foo, the contents of belaran@999: foo.orig will be clobbered. belaran@999: belaran@999: belaran@999: Here is a summary of the cases that the hg revert command can deal with. We belaran@999: will describe each of these in more detail in the section that belaran@999: follows. belaran@999: belaran@999: If you modify a file, it will restore the file belaran@999: to its unmodified state. belaran@999: belaran@999: If you hg add a belaran@999: file, it will undo the added state of the belaran@999: file, but leave the file itself untouched. belaran@999: belaran@999: If you delete a file without telling Mercurial, belaran@999: it will restore the file to its unmodified contents. belaran@999: belaran@999: If you use the hg belaran@999: remove command to remove a file, it will undo belaran@999: the removed state of the file, and restore belaran@999: the file to its unmodified contents. belaran@999: belaran@999: belaran@999: belaran@999: File management errors belaran@999: belaran@999: The hg revert command is belaran@999: useful for more than just modified files. It lets you reverse belaran@999: the results of all of Mercurial's file management belaran@999: commands—hg add, belaran@999: hg remove, and so on. belaran@999: belaran@999: If you hg add a file, belaran@999: then decide that in fact you don't want Mercurial to track it, belaran@999: use hg revert to undo the belaran@999: add. Don't worry; Mercurial will not modify the file in any belaran@999: way. It will just unmark the file. belaran@999: belaran@999: belaran@999: $ echo oops > oops belaran@999: $ hg add oops belaran@999: $ hg status oops belaran@999: A oops belaran@999: $ hg revert oops belaran@999: $ hg status belaran@999: ? oops belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Similarly, if you ask Mercurial to hg remove a file, you can use belaran@999: hg revert to restore it to belaran@999: the contents it had as of the parent of the working directory. belaran@999: belaran@999: $ hg remove file belaran@999: $ hg status belaran@999: R file belaran@999: $ hg revert file belaran@999: $ hg status belaran@999: $ ls file belaran@999: file belaran@999: belaran@999: belaran@999: This works just as belaran@999: well for a file that you deleted by hand, without telling belaran@999: Mercurial (recall that in Mercurial terminology, this kind of belaran@999: file is called missing). belaran@999: belaran@999: belaran@999: $ rm file belaran@999: $ hg status belaran@999: ! file belaran@999: $ hg revert file belaran@999: $ ls file belaran@999: file belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If you revert a hg copy, belaran@999: the copied-to file remains in your working directory belaran@999: afterwards, untracked. Since a copy doesn't affect the belaran@999: copied-from file in any way, Mercurial doesn't do anything belaran@999: with the copied-from file. belaran@999: belaran@999: belaran@999: $ hg copy file new-file belaran@999: $ hg revert new-file belaran@999: $ hg status belaran@999: ? new-file belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Dealing with committed changes belaran@999: belaran@999: Consider a case where you have committed a change belaran@999: a, and another change belaran@999: b on top of it; you then realise that belaran@999: change a was incorrect. Mercurial lets you belaran@999: back out an entire changeset automatically, and belaran@999: building blocks that let you reverse part of a changeset by belaran@999: hand. belaran@999: belaran@999: Before you read this section, here's something to belaran@999: keep in mind: the hg backout belaran@999: command undoes the effect of a change by belaran@999: adding to your repository's history, not by belaran@999: modifying or erasing it. It's the right tool to use if you're belaran@999: fixing bugs, but not if you're trying to undo some change that belaran@999: has catastrophic consequences. To deal with those, see belaran@999: . belaran@999: belaran@999: belaran@999: Backing out a changeset belaran@999: belaran@999: The hg backout command belaran@999: lets you undo the effects of an entire belaran@999: changeset in an automated fashion. Because Mercurial's belaran@999: history is immutable, this command does belaran@999: not get rid of the changeset you want to undo. belaran@999: Instead, it creates a new changeset that belaran@999: reverses the effect of the to-be-undone belaran@999: changeset. belaran@999: belaran@999: The operation of the hg belaran@999: backout command is a little intricate, so let's belaran@999: illustrate it with some examples. First, we'll create a belaran@999: repository with some simple changes. belaran@999: belaran@999: belaran@999: $ hg init myrepo belaran@999: $ cd myrepo belaran@999: $ echo first change >> myfile belaran@999: $ hg add myfile belaran@999: $ hg commit -m 'first change' belaran@999: $ echo second change >> myfile belaran@999: $ hg commit -m 'second change' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The hg backout command belaran@999: takes a single changeset ID as its argument; this is the belaran@999: changeset to back out. Normally, hg belaran@999: backout will drop you into a text editor to write belaran@999: a commit message, so you can record why you're backing the belaran@999: change out. In this example, we provide a commit message on belaran@999: the command line using the option. belaran@999: belaran@999: belaran@999: belaran@999: Backing out the tip changeset belaran@999: belaran@999: We're going to start by backing out the last changeset we belaran@999: committed. belaran@999: belaran@999: belaran@999: $ hg backout -m 'back out second change' tip belaran@999: reverting myfile belaran@999: changeset 2:611a0cae251c backs out changeset 1:43700a9b3ec8 belaran@999: $ cat myfile belaran@999: first change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You can see that the second line from belaran@999: myfile is no longer present. Taking a belaran@999: look at the output of hg log belaran@999: gives us an idea of what the hg belaran@999: backout command has done. belaran@999: belaran@999: $ hg log --style compact belaran@999: 2[tip] 611a0cae251c 2009-08-16 14:04 +0000 bos belaran@999: back out second change belaran@999: belaran@999: 1 43700a9b3ec8 2009-08-16 14:04 +0000 bos belaran@999: second change belaran@999: belaran@999: 0 f2ef23d503fd 2009-08-16 14:04 +0000 bos belaran@999: first change belaran@999: belaran@999: belaran@999: belaran@999: Notice that the new changeset belaran@999: that hg backout has created belaran@999: is a child of the changeset we backed out. It's easier to see belaran@999: this in , which presents a belaran@999: graphical view of the change history. As you can see, the belaran@999: history is nice and linear. belaran@999: belaran@999:
belaran@999: Backing out a change using the <command role="hg-cmd" moreinfo="none">hg backout</command> command belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999:
belaran@999: belaran@999: Backing out a non-tip change belaran@999: belaran@999: If you want to back out a change other than the last one belaran@999: you committed, pass the option to the belaran@999: hg backout command. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone -r1 myrepo non-tip-repo belaran@999: requesting all changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 2 changesets with 2 changes to 1 files belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd non-tip-repo belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This makes backing out any changeset a belaran@999: one-shot operation that's usually simple and belaran@999: fast. belaran@999: belaran@999: belaran@999: $ echo third change >> myfile belaran@999: $ hg commit -m 'third change' belaran@999: $ hg backout --merge -m 'back out second change' 1 belaran@999: reverting myfile belaran@999: created new head belaran@999: changeset 3:611a0cae251c backs out changeset 1:43700a9b3ec8 belaran@999: merging with changeset 3:611a0cae251c belaran@999: merging myfile belaran@999: 0 files updated, 1 files merged, 0 files removed, 0 files unresolved belaran@999: (branch merge, don't forget to commit) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If you take a look at the contents of belaran@999: myfile after the backout finishes, you'll belaran@999: see that the first and third changes are present, but not the belaran@999: second. belaran@999: belaran@999: belaran@999: $ cat myfile belaran@999: first change belaran@999: third change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: As the graphical history in illustrates, Mercurial belaran@999: still commits one change in this kind of situation (the belaran@999: box-shaped node is the ones that Mercurial commits belaran@999: automatically), but the revision graph now looks different. belaran@999: Before Mercurial begins the backout process, it first belaran@999: remembers what the current parent of the working directory is. belaran@999: It then backs out the target changeset, and commits that as a belaran@999: changeset. Finally, it merges back to the previous parent of belaran@999: the working directory, but notice that it does not belaran@999: commit the result of the merge. The repository belaran@999: now contains two heads, and the working directory is in a belaran@999: merge state. belaran@999: belaran@999:
belaran@999: Automated backout of a non-tip change using the belaran@999: <command role="hg-cmd" moreinfo="none">hg backout</command> command belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: The result is that you end up back where you belaran@999: were, only with some extra history that undoes the belaran@999: effect of the changeset you wanted to back out. belaran@999: belaran@999: You might wonder why Mercurial does not commit the result belaran@999: of the merge that it performed. The reason lies in Mercurial belaran@999: behaving conservatively: a merge naturally has more scope for belaran@999: error than simply undoing the effect of the tip changeset, belaran@999: so your work will be safest if you first inspect (and test!) belaran@999: the result of the merge, then commit belaran@999: it. belaran@999: belaran@999: belaran@999: Always use the <option role="hg-opt-backout">--merge</option> option belaran@999: belaran@999: In fact, since the option will do the belaran@999: right thing whether or not the changeset belaran@999: you're backing out is the tip (i.e. it won't try to merge if belaran@999: it's backing out the tip, since there's no need), you should belaran@999: always use this option when you run the belaran@999: hg backout command. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: Gaining more control of the backout process belaran@999: belaran@999: While I've recommended that you always use the option when backing belaran@999: out a change, the hg backout belaran@999: command lets you decide how to merge a backout changeset. belaran@999: Taking control of the backout process by hand is something you belaran@999: will rarely need to do, but it can be useful to understand belaran@999: what the hg backout command belaran@999: is doing for you automatically. To illustrate this, let's belaran@999: clone our first repository, but omit the backout change that belaran@999: it contains. belaran@999: belaran@999: belaran@999: $ cd .. belaran@999: $ hg clone -r1 myrepo newrepo belaran@999: requesting all changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 2 changesets with 2 changes to 1 files belaran@999: updating working directory belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd newrepo belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: As with our belaran@999: earlier example, We'll commit a third changeset, then back out belaran@999: its parent, and see what happens. belaran@999: belaran@999: belaran@999: $ echo third change >> myfile belaran@999: $ hg commit -m 'third change' belaran@999: $ hg backout -m 'back out second change' 1 belaran@999: reverting myfile belaran@999: created new head belaran@999: changeset 3:bf906ee0baae backs out changeset 1:43700a9b3ec8 belaran@999: the backout changeset is a new head - do not forget to merge belaran@999: (use "backout --merge" if you want to auto-merge) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Our new changeset is again a descendant of the changeset belaran@999: we backout out; it's thus a new head, not belaran@999: a descendant of the changeset that was the tip. The hg backout command was quite belaran@999: explicit in telling us this. belaran@999: belaran@999: belaran@999: $ hg log --style compact belaran@999: 3[tip]:1 bf906ee0baae 2009-08-16 14:04 +0000 bos belaran@999: back out second change belaran@999: belaran@999: 2 2521379001ad 2009-08-16 14:04 +0000 bos belaran@999: third change belaran@999: belaran@999: 1 43700a9b3ec8 2009-08-16 14:04 +0000 bos belaran@999: second change belaran@999: belaran@999: 0 f2ef23d503fd 2009-08-16 14:04 +0000 bos belaran@999: first change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Again, it's easier to see what has happened by looking at belaran@999: a graph of the revision history, in . This makes it clear belaran@999: that when we use hg backout belaran@999: to back out a change other than the tip, Mercurial adds a new belaran@999: head to the repository (the change it committed is belaran@999: box-shaped). belaran@999: belaran@999:
belaran@999: Backing out a change using the <command role="hg-cmd" moreinfo="none">hg backout</command> command belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: After the hg backout belaran@999: command has completed, it leaves the new belaran@999: backout changeset as the parent of the working belaran@999: directory. belaran@999: belaran@999: belaran@999: $ hg parents belaran@999: changeset: 2:2521379001ad belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:37 2009 +0000 belaran@999: summary: third change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Now we have two isolated sets of changes. belaran@999: belaran@999: belaran@999: $ hg heads belaran@999: changeset: 3:bf906ee0baae belaran@999: tag: tip belaran@999: parent: 1:43700a9b3ec8 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:37 2009 +0000 belaran@999: summary: back out second change belaran@999: belaran@999: changeset: 2:2521379001ad belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:37 2009 +0000 belaran@999: summary: third change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Let's think about what we expect to see as the contents of belaran@999: myfile now. The first change should be belaran@999: present, because we've never backed it out. The second change belaran@999: should be missing, as that's the change we backed out. Since belaran@999: the history graph shows the third change as a separate head, belaran@999: we don't expect to see the third change belaran@999: present in myfile. belaran@999: belaran@999: belaran@999: $ cat myfile belaran@999: first change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: To get the third change back into the file, we just do a belaran@999: normal merge of our two heads. belaran@999: belaran@999: belaran@999: $ hg merge belaran@999: abort: outstanding uncommitted changes belaran@999: $ hg commit -m 'merged backout with previous tip' belaran@999: $ cat myfile belaran@999: first change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Afterwards, the graphical history of our belaran@999: repository looks like belaran@999: . belaran@999: belaran@999:
belaran@999: Manually merging a backout change belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999:
belaran@999: belaran@999: Why <command role="hg-cmd" moreinfo="none">hg backout</command> works as belaran@999: it does belaran@999: belaran@999: Here's a brief description of how the hg backout command works. belaran@999: belaran@999: It ensures that the working directory is belaran@999: clean, i.e. that the output of hg status would be empty. belaran@999: belaran@999: It remembers the current parent of the working belaran@999: directory. Let's call this changeset belaran@999: orig. belaran@999: belaran@999: It does the equivalent of a hg update to sync the working belaran@999: directory to the changeset you want to back out. Let's belaran@999: call this changeset backout. belaran@999: belaran@999: It finds the parent of that changeset. Let's belaran@999: call that changeset parent. belaran@999: belaran@999: For each file that the belaran@999: backout changeset affected, it does the belaran@999: equivalent of a hg revert -r belaran@999: parent on that file, to restore it to the belaran@999: contents it had before that changeset was belaran@999: committed. belaran@999: belaran@999: It commits the result as a new changeset. belaran@999: This changeset has backout as its belaran@999: parent. belaran@999: belaran@999: If you specify on the command belaran@999: line, it merges with orig, and commits belaran@999: the result of the merge. belaran@999: belaran@999: belaran@999: An alternative way to implement the hg backout command would be to belaran@999: hg export the belaran@999: to-be-backed-out changeset as a diff, then use the option to the belaran@999: patch command to reverse the effect of the belaran@999: change without fiddling with the working directory. This belaran@999: sounds much simpler, but it would not work nearly as belaran@999: well. belaran@999: belaran@999: The reason that hg belaran@999: backout does an update, a commit, a merge, and belaran@999: another commit is to give the merge machinery the best chance belaran@999: to do a good job when dealing with all the changes belaran@999: between the change you're backing out and belaran@999: the current tip. belaran@999: belaran@999: If you're backing out a changeset that's 100 revisions belaran@999: back in your project's history, the chances that the belaran@999: patch command will be able to apply a belaran@999: reverse diff cleanly are not good, because intervening changes belaran@999: are likely to have broken the context that belaran@999: patch uses to determine whether it can belaran@999: apply a patch (if this sounds like gibberish, see for a belaran@999: discussion of the patch command). Also, belaran@999: Mercurial's merge machinery will handle files and directories belaran@999: being renamed, permission changes, and modifications to binary belaran@999: files, none of which patch can deal belaran@999: with. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: Changes that should never have been belaran@999: belaran@999: Most of the time, the hg belaran@999: backout command is exactly what you need if you want belaran@999: to undo the effects of a change. It leaves a permanent record belaran@999: of exactly what you did, both when committing the original belaran@999: changeset and when you cleaned up after it. belaran@999: belaran@999: On rare occasions, though, you may find that you've belaran@999: committed a change that really should not be present in the belaran@999: repository at all. For example, it would be very unusual, and belaran@999: usually considered a mistake, to commit a software project's belaran@999: object files as well as its source files. Object files have belaran@999: almost no intrinsic value, and they're big, belaran@999: so they increase the size of the repository and the amount of belaran@999: time it takes to clone or pull changes. belaran@999: belaran@999: Before I discuss the options that you have if you commit a belaran@999: brown paper bag change (the kind that's so bad belaran@999: that you want to pull a brown paper bag over your head), let me belaran@999: first discuss some approaches that probably won't work. belaran@999: belaran@999: Since Mercurial treats history as belaran@999: accumulative—every change builds on top of all changes belaran@999: that preceded it—you generally can't just make disastrous belaran@999: changes disappear. The one exception is when you've just belaran@999: committed a change, and it hasn't been pushed or pulled into belaran@999: another repository. That's when you can safely use the hg rollback command, as I detailed in belaran@999: . belaran@999: belaran@999: After you've pushed a bad change to another repository, you belaran@999: could still use hg belaran@999: rollback to make your local copy of the change belaran@999: disappear, but it won't have the consequences you want. The belaran@999: change will still be present in the remote repository, so it belaran@999: will reappear in your local repository the next time you belaran@999: pull. belaran@999: belaran@999: If a situation like this arises, and you know which belaran@999: repositories your bad change has propagated into, you can belaran@999: try to get rid of the change from belaran@999: every one of those repositories. This is, belaran@999: of course, not a satisfactory solution: if you miss even a belaran@999: single repository while you're expunging, the change is still belaran@999: in the wild, and could propagate further. belaran@999: belaran@999: If you've committed one or more changes belaran@999: after the change that you'd like to see belaran@999: disappear, your options are further reduced. Mercurial doesn't belaran@999: provide a way to punch a hole in history, leaving belaran@999: changesets intact. belaran@999: belaran@999: belaran@999: Backing out a merge belaran@999: belaran@999: Since merges are often complicated, it is not unheard of belaran@999: for a merge to be mangled badly, but committed erroneously. belaran@999: Mercurial provides an important safeguard against bad merges belaran@999: by refusing to commit unresolved files, but human ingenuity belaran@999: guarantees that it is still possible to mess a merge up and belaran@999: commit it. belaran@999: belaran@999: Given a bad merge that has been committed, usually the belaran@999: best way to approach it is to simply try to repair the damage belaran@999: by hand. A complete disaster that cannot be easily fixed up belaran@999: by hand ought to be very rare, but the hg backout command may help in belaran@999: making the cleanup easier. It offers a option, which lets belaran@999: you specify which parent to revert to when backing out a belaran@999: merge. belaran@999: belaran@999:
belaran@999: A bad merge belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: Suppose we have a revision graph like that in . What we'd like is to belaran@999: redo the merge of revisions 2 and belaran@999: 3. belaran@999: belaran@999: One way to do so would be as follows. belaran@999: belaran@999: belaran@999: belaran@999: Call hg backout --rev=4 belaran@999: --parent=2. This tells hg backout to back out revision belaran@999: 4, which is the bad merge, and to when deciding which belaran@999: revision to prefer, to choose parent 2, one of the parents belaran@999: of the merge. The effect can be seen in . belaran@999:
belaran@999: Backing out the merge, favoring one parent belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999:
belaran@999: belaran@999: belaran@999: Call hg backout --rev=4 belaran@999: --parent=3. This tells hg backout to back out revision belaran@999: 4 again, but this time to choose parent 3, the other belaran@999: parent of the merge. The result is visible in , in which the repository belaran@999: now contains three heads. belaran@999:
belaran@999: Backing out the merge, favoring the other belaran@999: parent belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999:
belaran@999: belaran@999: belaran@999: Redo the bad merge by merging the two backout heads, belaran@999: which reduces the number of heads in the repository to belaran@999: two, as can be seen in . belaran@999:
belaran@999: Merging the backouts belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999:
belaran@999: belaran@999: belaran@999: Merge with the commit that was made after the bad belaran@999: merge, as shown in . belaran@999:
belaran@999: Merging the backouts belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999:
belaran@999:
belaran@999:
belaran@999: belaran@999: belaran@999: Protect yourself from <quote>escaped</quote> belaran@999: changes belaran@999: belaran@999: If you've committed some changes to your local repository belaran@999: and they've been pushed or pulled somewhere else, this isn't belaran@999: necessarily a disaster. You can protect yourself ahead of belaran@999: time against some classes of bad changeset. This is belaran@999: particularly easy if your team usually pulls changes from a belaran@999: central repository. belaran@999: belaran@999: By configuring some hooks on that repository to validate belaran@999: incoming changesets (see chapter ), belaran@999: you can belaran@999: automatically prevent some kinds of bad changeset from being belaran@999: pushed to the central repository at all. With such a belaran@999: configuration in place, some kinds of bad changeset will belaran@999: naturally tend to die out because they can't belaran@999: propagate into the central repository. Better yet, this belaran@999: happens without any need for explicit intervention. belaran@999: belaran@999: For instance, an incoming change hook that belaran@999: verifies that a changeset will actually compile can prevent belaran@999: people from inadvertently breaking the belaran@999: build. belaran@999: belaran@999: belaran@999: belaran@999: What to do about sensitive changes that escape belaran@999: belaran@999: Even a carefully run project can suffer an unfortunate belaran@999: event such as the committing and uncontrolled propagation of a belaran@999: file that contains important passwords. belaran@999: belaran@999: If something like this happens to you, and the information belaran@999: that gets accidentally propagated is truly sensitive, your belaran@999: first step should be to mitigate the effect of the leak belaran@999: without trying to control the leak itself. If you are not 100% belaran@999: certain that you know exactly who could have seen the changes, belaran@999: you should immediately change passwords, cancel credit cards, belaran@999: or find some other way to make sure that the information that belaran@999: has leaked is no longer useful. In other words, assume that belaran@999: the change has propagated far and wide, and that there's belaran@999: nothing more you can do. belaran@999: belaran@999: You might hope that there would be mechanisms you could belaran@999: use to either figure out who has seen a change or to erase the belaran@999: change permanently everywhere, but there are good reasons why belaran@999: these are not possible. belaran@999: belaran@999: Mercurial does not provide an audit trail of who has belaran@999: pulled changes from a repository, because it is usually either belaran@999: impossible to record such information or trivial to spoof it. belaran@999: In a multi-user or networked environment, you should thus be belaran@999: extremely skeptical of yourself if you think that you have belaran@999: identified every place that a sensitive changeset has belaran@999: propagated to. Don't forget that people can and will send belaran@999: bundles by email, have their backup software save data belaran@999: offsite, carry repositories on USB sticks, and find other belaran@999: completely innocent ways to confound your attempts to track belaran@999: down every copy of a problematic change. belaran@999: belaran@999: Mercurial also does not provide a way to make a file or belaran@999: changeset completely disappear from history, because there is belaran@999: no way to enforce its disappearance; someone could easily belaran@999: modify their copy of Mercurial to ignore such directives. In belaran@999: addition, even if Mercurial provided such a capability, belaran@999: someone who simply hadn't pulled a make this file belaran@999: disappear changeset wouldn't be affected by it, nor belaran@999: would web crawlers visiting at the wrong time, disk backups, belaran@999: or other mechanisms. Indeed, no distributed revision control belaran@999: system can make data reliably vanish. Providing the illusion belaran@999: of such control could easily give a false sense of security, belaran@999: and be worse than not providing it at all. belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: Finding the source of a bug belaran@999: belaran@999: While it's all very well to be able to back out a changeset belaran@999: that introduced a bug, this requires that you know which belaran@999: changeset to back out. Mercurial provides an invaluable belaran@999: command, called hg bisect, that belaran@999: helps you to automate this process and accomplish it very belaran@999: efficiently. belaran@999: belaran@999: The idea behind the hg belaran@999: bisect command is that a changeset has introduced belaran@999: some change of behavior that you can identify with a simple belaran@999: pass/fail test. You don't know which piece of code introduced the belaran@999: change, but you know how to test for the presence of the bug. belaran@999: The hg bisect command uses your belaran@999: test to direct its search for the changeset that introduced the belaran@999: code that caused the bug. belaran@999: belaran@999: Here are a few scenarios to help you understand how you belaran@999: might apply this command. belaran@999: belaran@999: The most recent version of your software has a belaran@999: bug that you remember wasn't present a few weeks ago, but belaran@999: you don't know when it was introduced. Here, your binary belaran@999: test checks for the presence of that bug. belaran@999: belaran@999: You fixed a bug in a rush, and now it's time to belaran@999: close the entry in your team's bug database. The bug belaran@999: database requires a changeset ID when you close an entry, belaran@999: but you don't remember which changeset you fixed the bug in. belaran@999: Once again, your binary test checks for the presence of the belaran@999: bug. belaran@999: belaran@999: Your software works correctly, but runs 15% belaran@999: slower than the last time you measured it. You want to know belaran@999: which changeset introduced the performance regression. In belaran@999: this case, your binary test measures the performance of your belaran@999: software, to see whether it's fast or belaran@999: slow. belaran@999: belaran@999: The sizes of the components of your project that belaran@999: you ship exploded recently, and you suspect that something belaran@999: changed in the way you build your project. belaran@999: belaran@999: belaran@999: From these examples, it should be clear that the hg bisect command is not useful only belaran@999: for finding the sources of bugs. You can use it to find any belaran@999: emergent property of a repository (anything that belaran@999: you can't find from a simple text search of the files in the belaran@999: tree) for which you can write a binary test. belaran@999: belaran@999: We'll introduce a little bit of terminology here, just to belaran@999: make it clear which parts of the search process are your belaran@999: responsibility, and which are Mercurial's. A belaran@999: test is something that belaran@999: you run when hg belaran@999: bisect chooses a changeset. A belaran@999: probe is what hg belaran@999: bisect runs to tell whether a revision is good. belaran@999: Finally, we'll use the word bisect, as both a belaran@999: noun and a verb, to stand in for the phrase search using belaran@999: the hg bisect belaran@999: command. belaran@999: belaran@999: One simple way to automate the searching process would be belaran@999: simply to probe every changeset. However, this scales poorly. belaran@999: If it took ten minutes to test a single changeset, and you had belaran@999: 10,000 changesets in your repository, the exhaustive approach belaran@999: would take on average 35 days to find the belaran@999: changeset that introduced a bug. Even if you knew that the bug belaran@999: was introduced by one of the last 500 changesets, and limited belaran@999: your search to those, you'd still be looking at over 40 hours to belaran@999: find the changeset that introduced your bug. belaran@999: belaran@999: What the hg bisect command belaran@999: does is use its knowledge of the shape of your belaran@999: project's revision history to perform a search in time belaran@999: proportional to the logarithm of the number belaran@999: of changesets to check (the kind of search it performs is called belaran@999: a dichotomic search). With this approach, searching through belaran@999: 10,000 changesets will take less than three hours, even at ten belaran@999: minutes per test (the search will require about 14 tests). belaran@999: Limit your search to the last hundred changesets, and it will belaran@999: take only about an hour (roughly seven tests). belaran@999: belaran@999: The hg bisect command is belaran@999: aware of the branchy nature of a Mercurial belaran@999: project's revision history, so it has no problems dealing with belaran@999: branches, merges, or multiple heads in a repository. It can belaran@999: prune entire branches of history with a single probe, which is belaran@999: how it operates so efficiently. belaran@999: belaran@999: belaran@999: Using the <command role="hg-cmd" moreinfo="none">hg bisect</command> belaran@999: command belaran@999: belaran@999: Here's an example of hg belaran@999: bisect in action. belaran@999: belaran@999: belaran@999: In versions 0.9.5 and earlier of Mercurial, hg bisect was not a core command: belaran@999: it was distributed with Mercurial as an extension. This belaran@999: section describes the built-in command, not the old belaran@999: extension. belaran@999: belaran@999: belaran@999: Now let's create a repository, so that we can try out the belaran@999: hg bisect command in belaran@999: isolation. belaran@999: belaran@999: belaran@999: $ hg init mybug belaran@999: $ cd mybug belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: We'll simulate a project that has a bug in it in a belaran@999: simple-minded way: create trivial changes in a loop, and belaran@999: nominate one specific change that will have the belaran@999: bug. This loop creates 35 changesets, each belaran@999: adding a single file to the repository. We'll represent our belaran@999: bug with a file that contains the text i belaran@999: have a gub. belaran@999: belaran@999: belaran@999: $ buggy_change=22 belaran@999: $ for (( i = 0; i < 35; i++ )); do belaran@999: > if [[ $i = $buggy_change ]]; then belaran@999: > echo 'i have a gub' > myfile$i belaran@999: > hg commit -q -A -m 'buggy changeset' belaran@999: > else belaran@999: > echo 'nothing to see here, move along' > myfile$i belaran@999: > hg commit -q -A -m 'normal changeset' belaran@999: > fi belaran@999: > done belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The next thing that we'd like to do is figure out how to belaran@999: use the hg bisect command. belaran@999: We can use Mercurial's normal built-in help mechanism for belaran@999: this. belaran@999: belaran@999: belaran@999: $ hg help bisect belaran@999: hg bisect [-gbsr] [-c CMD] [REV] belaran@999: belaran@999: subdivision search of changesets belaran@999: belaran@999: This command helps to find changesets which introduce problems. belaran@999: To use, mark the earliest changeset you know exhibits the problem belaran@999: as bad, then mark the latest changeset which is free from the belaran@999: problem as good. Bisect will update your working directory to a belaran@999: revision for testing (unless the --noupdate option is specified). belaran@999: Once you have performed tests, mark the working directory as bad belaran@999: or good and bisect will either update to another candidate changeset belaran@999: or announce that it has found the bad revision. belaran@999: belaran@999: As a shortcut, you can also use the revision argument to mark a belaran@999: revision as good or bad without checking it out first. belaran@999: belaran@999: If you supply a command it will be used for automatic bisection. Its exit belaran@999: status will be used as flag to mark revision as bad or good. In case exit belaran@999: status is 0 the revision is marked as good, 125 - skipped, 127 (command not belaran@999: found) - bisection will be aborted; any other status bigger than 0 will belaran@999: mark revision as bad. belaran@999: belaran@999: options: belaran@999: belaran@999: -r --reset reset bisect state belaran@999: -g --good mark changeset good belaran@999: -b --bad mark changeset bad belaran@999: -s --skip skip testing changeset belaran@999: -c --command use command to check changeset state belaran@999: -U --noupdate do not update to target belaran@999: belaran@999: use "hg -v help bisect" to show global options belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The hg bisect command belaran@999: works in steps. Each step proceeds as follows. belaran@999: belaran@999: You run your binary test. belaran@999: belaran@999: If the test succeeded, you tell hg bisect by running the belaran@999: hg bisect --good belaran@999: command. belaran@999: belaran@999: If it failed, run the hg bisect --bad belaran@999: command. belaran@999: belaran@999: The command uses your information to decide belaran@999: which changeset to test next. belaran@999: belaran@999: It updates the working directory to that belaran@999: changeset, and the process begins again. belaran@999: belaran@999: The process ends when hg belaran@999: bisect identifies a unique changeset that marks belaran@999: the point where your test transitioned from belaran@999: succeeding to failing. belaran@999: belaran@999: To start the search, we must run the hg bisect --reset command. belaran@999: belaran@999: belaran@999: $ hg bisect --reset belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: In our case, the binary test we use is simple: we check to belaran@999: see if any file in the repository contains the string i belaran@999: have a gub. If it does, this changeset contains the belaran@999: change that caused the bug. By convention, a belaran@999: changeset that has the property we're searching for is belaran@999: bad, while one that doesn't is belaran@999: good. belaran@999: belaran@999: Most of the time, the revision to which the working belaran@999: directory is synced (usually the tip) already exhibits the belaran@999: problem introduced by the buggy change, so we'll mark it as belaran@999: bad. belaran@999: belaran@999: belaran@999: $ hg bisect --bad belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Our next task is to nominate a changeset that we know belaran@999: doesn't have the bug; the hg bisect command will belaran@999: bracket its search between the first pair of belaran@999: good and bad changesets. In our case, we know that revision belaran@999: 10 didn't have the bug. (I'll have more words about choosing belaran@999: the first good changeset later.) belaran@999: belaran@999: belaran@999: $ hg bisect --good 10 belaran@999: Testing changeset 22:69f52b967ab8 (24 changesets remaining, ~4 tests) belaran@999: 0 files updated, 0 files merged, 12 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Notice that this command printed some output. belaran@999: belaran@999: It told us how many changesets it must belaran@999: consider before it can identify the one that introduced belaran@999: the bug, and how many tests that will require. belaran@999: belaran@999: It updated the working directory to the next belaran@999: changeset to test, and told us which changeset it's belaran@999: testing. belaran@999: belaran@999: belaran@999: We now run our test in the working directory. We use the belaran@999: grep command to see if our belaran@999: bad file is present in the working directory. belaran@999: If it is, this revision is bad; if not, this revision is good. belaran@999: belaran@999: $ if grep -q 'i have a gub' * belaran@999: > then belaran@999: > result=bad belaran@999: > else belaran@999: > result=good belaran@999: > fi belaran@999: $ echo this revision is $result belaran@999: this revision is bad belaran@999: $ hg bisect --$result belaran@999: Testing changeset 16:f1dd8bc690ae (12 changesets remaining, ~3 tests) belaran@999: 0 files updated, 0 files merged, 6 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This test looks like a perfect candidate for automation, belaran@999: so let's turn it into a shell function. belaran@999: belaran@999: $ mytest() { belaran@999: > if grep -q 'i have a gub' * belaran@999: > then belaran@999: > result=bad belaran@999: > else belaran@999: > result=good belaran@999: > fi belaran@999: > echo this revision is $result belaran@999: > hg bisect --$result belaran@999: > } belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: We can now run an entire test step with a single command, belaran@999: mytest. belaran@999: belaran@999: belaran@999: $ mytest belaran@999: this revision is good belaran@999: Testing changeset 19:88d99d97058a (6 changesets remaining, ~2 tests) belaran@999: 3 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: A few more invocations of our canned test step command, belaran@999: and we're done. belaran@999: belaran@999: belaran@999: $ mytest belaran@999: this revision is good belaran@999: Testing changeset 20:32a195a31d51 (3 changesets remaining, ~1 tests) belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ mytest belaran@999: this revision is good belaran@999: Testing changeset 21:a2efe8e4f624 (2 changesets remaining, ~1 tests) belaran@999: 1 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ mytest belaran@999: this revision is good belaran@999: The first bad revision is: belaran@999: changeset: 22:69f52b967ab8 belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:04:39 2009 +0000 belaran@999: summary: buggy changeset belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Even though we had 40 changesets to search through, the belaran@999: hg bisect command let us find belaran@999: the changeset that introduced our bug with only belaran@999: five tests. Because the number of tests that the hg bisect command performs grows belaran@999: logarithmically with the number of changesets to search, the belaran@999: advantage that it has over the brute force belaran@999: search approach increases with every changeset you add. belaran@999: belaran@999: belaran@999: belaran@999: Cleaning up after your search belaran@999: belaran@999: When you're finished using the hg belaran@999: bisect command in a repository, you can use the belaran@999: hg bisect --reset command to belaran@999: drop the information it was using to drive your search. The belaran@999: command doesn't use much space, so it doesn't matter if you belaran@999: forget to run this command. However, hg bisect won't let you start a new belaran@999: search in that repository until you do a hg bisect --reset. belaran@999: belaran@999: belaran@999: $ hg bisect --reset belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Tips for finding bugs effectively belaran@999: belaran@999: belaran@999: Give consistent input belaran@999: belaran@999: The hg bisect command belaran@999: requires that you correctly report the result of every test belaran@999: you perform. If you tell it that a test failed when it really belaran@999: succeeded, it might be able to detect the belaran@999: inconsistency. If it can identify an inconsistency in your belaran@999: reports, it will tell you that a particular changeset is both belaran@999: good and bad. However, it can't do this perfectly; it's about belaran@999: as likely to report the wrong changeset as the source of the belaran@999: bug. belaran@999: belaran@999: belaran@999: belaran@999: Automate as much as possible belaran@999: belaran@999: When I started using the hg belaran@999: bisect command, I tried a few times to run my belaran@999: tests by hand, on the command line. This is an approach that belaran@999: I, at least, am not suited to. After a few tries, I found belaran@999: that I was making enough mistakes that I was having to restart belaran@999: my searches several times before finally getting correct belaran@999: results. belaran@999: belaran@999: My initial problems with driving the hg bisect command by hand occurred belaran@999: even with simple searches on small repositories; if the belaran@999: problem you're looking for is more subtle, or the number of belaran@999: tests that hg bisect must belaran@999: perform increases, the likelihood of operator error ruining belaran@999: the search is much higher. Once I started automating my belaran@999: tests, I had much better results. belaran@999: belaran@999: The key to automated testing is twofold: belaran@999: belaran@999: always test for the same symptom, and belaran@999: belaran@999: always feed consistent input to the hg bisect command. belaran@999: belaran@999: In my tutorial example above, the grep belaran@999: command tests for the symptom, and the if belaran@999: statement takes the result of this check and ensures that we belaran@999: always feed the same input to the hg belaran@999: bisect command. The mytest belaran@999: function marries these together in a reproducible way, so that belaran@999: every test is uniform and consistent. belaran@999: belaran@999: belaran@999: belaran@999: Check your results belaran@999: belaran@999: Because the output of a hg belaran@999: bisect search is only as good as the input you belaran@999: give it, don't take the changeset it reports as the absolute belaran@999: truth. A simple way to cross-check its report is to manually belaran@999: run your test at each of the following changesets: belaran@999: belaran@999: The changeset that it reports as the first bad belaran@999: revision. Your test should still report this as belaran@999: bad. belaran@999: belaran@999: The parent of that changeset (either parent, belaran@999: if it's a merge). Your test should report this changeset belaran@999: as good. belaran@999: belaran@999: A child of that changeset. Your test should belaran@999: report this changeset as bad. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Beware interference between bugs belaran@999: belaran@999: It's possible that your search for one bug could be belaran@999: disrupted by the presence of another. For example, let's say belaran@999: your software crashes at revision 100, and worked correctly at belaran@999: revision 50. Unknown to you, someone else introduced a belaran@999: different crashing bug at revision 60, and fixed it at belaran@999: revision 80. This could distort your results in one of belaran@999: several ways. belaran@999: belaran@999: It is possible that this other bug completely belaran@999: masks yours, which is to say that it occurs belaran@999: before your bug has a chance to manifest itself. If you can't belaran@999: avoid that other bug (for example, it prevents your project belaran@999: from building), and so can't tell whether your bug is present belaran@999: in a particular changeset, the hg belaran@999: bisect command cannot help you directly. Instead, belaran@999: you can mark a changeset as untested by running hg bisect --skip. belaran@999: belaran@999: A different problem could arise if your test for a bug's belaran@999: presence is not specific enough. If you check for my belaran@999: program crashes, then both your crashing bug and an belaran@999: unrelated crashing bug that masks it will look like the same belaran@999: thing, and mislead hg belaran@999: bisect. belaran@999: belaran@999: Another useful situation in which to use hg bisect --skip is if you can't belaran@999: test a revision because your project was in a broken and hence belaran@999: untestable state at that revision, perhaps because someone belaran@999: checked in a change that prevented the project from belaran@999: building. belaran@999: belaran@999: belaran@999: belaran@999: Bracket your search lazily belaran@999: belaran@999: Choosing the first good and belaran@999: bad changesets that will mark the end points of belaran@999: your search is often easy, but it bears a little discussion belaran@999: nevertheless. From the perspective of hg bisect, the newest belaran@999: changeset is conventionally bad, and the older belaran@999: changeset is good. belaran@999: belaran@999: If you're having trouble remembering when a suitable belaran@999: good change was, so that you can tell hg bisect, you could do worse than belaran@999: testing changesets at random. Just remember to eliminate belaran@999: contenders that can't possibly exhibit the bug (perhaps belaran@999: because the feature with the bug isn't present yet) and those belaran@999: where another problem masks the bug (as I discussed belaran@999: above). belaran@999: belaran@999: Even if you end up early by thousands of belaran@999: changesets or months of history, you will only add a handful belaran@999: of tests to the total number that hg belaran@999: bisect must perform, thanks to its logarithmic belaran@999: behavior. belaran@999: belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Handling repository events with hooks belaran@999: belaran@999: Mercurial offers a powerful mechanism to let you perform belaran@999: automated actions in response to events that occur in a belaran@999: repository. In some cases, you can even control Mercurial's belaran@999: response to those events. belaran@999: belaran@999: The name Mercurial uses for one of these actions is a belaran@999: hook. Hooks are called belaran@999: triggers in some revision control systems, but the belaran@999: two names refer to the same idea. belaran@999: belaran@999: belaran@999: An overview of hooks in Mercurial belaran@999: belaran@999: Here is a brief list of the hooks that Mercurial belaran@999: supports. We will revisit each of these hooks in more detail belaran@999: later, in . belaran@999: belaran@999: Each of the hooks whose description begins with the word belaran@999: Controlling has the ability to determine whether belaran@999: an activity can proceed. If the hook succeeds, the activity may belaran@999: proceed; if it fails, the activity is either not permitted or belaran@999: undone, depending on the hook. belaran@999: belaran@999: belaran@999: changegroup: This belaran@999: is run after a group of changesets has been brought into the belaran@999: repository from elsewhere. belaran@999: belaran@999: commit: This is belaran@999: run after a new changeset has been created in the local belaran@999: repository. belaran@999: belaran@999: incoming: This is belaran@999: run once for each new changeset that is brought into the belaran@999: repository from elsewhere. Notice the difference from belaran@999: changegroup, which is run belaran@999: once per group of changesets brought belaran@999: in. belaran@999: belaran@999: outgoing: This is belaran@999: run after a group of changesets has been transmitted from belaran@999: this repository. belaran@999: belaran@999: prechangegroup: belaran@999: This is run before starting to bring a group of changesets belaran@999: into the repository. belaran@999: belaran@999: belaran@999: precommit: belaran@999: Controlling. This is run before starting a commit. belaran@999: belaran@999: belaran@999: preoutgoing: belaran@999: Controlling. This is run before starting to transmit a group belaran@999: of changesets from this repository. belaran@999: belaran@999: belaran@999: pretag: belaran@999: Controlling. This is run before creating a tag. belaran@999: belaran@999: belaran@999: pretxnchangegroup: Controlling. This belaran@999: is run after a group of changesets has been brought into the belaran@999: local repository from another, but before the transaction belaran@999: completes that will make the changes permanent in the belaran@999: repository. belaran@999: belaran@999: belaran@999: pretxncommit: belaran@999: Controlling. This is run after a new changeset has been belaran@999: created in the local repository, but before the transaction belaran@999: completes that will make it permanent. belaran@999: belaran@999: belaran@999: preupdate: belaran@999: Controlling. This is run before starting an update or merge belaran@999: of the working directory. belaran@999: belaran@999: belaran@999: tag: This is run belaran@999: after a tag is created. belaran@999: belaran@999: belaran@999: update: This is belaran@999: run after an update or merge of the working directory has belaran@999: finished. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Hooks and security belaran@999: belaran@999: belaran@999: Hooks are run with your privileges belaran@999: belaran@999: When you run a Mercurial command in a repository, and the belaran@999: command causes a hook to run, that hook runs on belaran@999: your system, under belaran@999: your user account, with belaran@999: your privilege level. Since hooks are belaran@999: arbitrary pieces of executable code, you should treat them belaran@999: with an appropriate level of suspicion. Do not install a hook belaran@999: unless you are confident that you know who created it and what belaran@999: it does. belaran@999: belaran@999: belaran@999: In some cases, you may be exposed to hooks that you did belaran@999: not install yourself. If you work with Mercurial on an belaran@999: unfamiliar system, Mercurial will run hooks defined in that belaran@999: system's global ~/.hgrc belaran@999: file. belaran@999: belaran@999: belaran@999: If you are working with a repository owned by another belaran@999: user, Mercurial can run hooks defined in that user's belaran@999: repository, but it will still run them as you. belaran@999: For example, if you hg pull belaran@999: from that repository, and its .hg/hgrc defines a local outgoing hook, that hook will run belaran@999: under your user account, even though you don't own that belaran@999: repository. belaran@999: belaran@999: belaran@999: belaran@999: This only applies if you are pulling from a repository belaran@999: on a local or network filesystem. If you're pulling over belaran@999: http or ssh, any outgoing belaran@999: hook will run under whatever account is executing the server belaran@999: process, on the server. belaran@999: belaran@999: belaran@999: belaran@999: To see what hooks are defined in a repository, belaran@999: use the hg showconfig hooks belaran@999: command. If you are working in one repository, but talking to belaran@999: another that you do not own (e.g. using hg pull or hg belaran@999: incoming), remember that it is the other belaran@999: repository's hooks you should be checking, not your own. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Hooks do not propagate belaran@999: belaran@999: In Mercurial, hooks are not revision controlled, and do belaran@999: not propagate when you clone, or pull from, a repository. The belaran@999: reason for this is simple: a hook is a completely arbitrary belaran@999: piece of executable code. It runs under your user identity, belaran@999: with your privilege level, on your machine. belaran@999: belaran@999: belaran@999: It would be extremely reckless for any distributed belaran@999: revision control system to implement revision-controlled belaran@999: hooks, as this would offer an easily exploitable way to belaran@999: subvert the accounts of users of the revision control system. belaran@999: belaran@999: belaran@999: Since Mercurial does not propagate hooks, if you are belaran@999: collaborating with other people on a common project, you belaran@999: should not assume that they are using the same Mercurial hooks belaran@999: as you are, or that theirs are correctly configured. You belaran@999: should document the hooks you expect people to use. belaran@999: belaran@999: belaran@999: In a corporate intranet, this is somewhat easier to belaran@999: control, as you can for example provide a belaran@999: standard installation of Mercurial on an NFS belaran@999: filesystem, and use a site-wide ~/.hgrc file to define hooks that all users will belaran@999: see. However, this too has its limits; see below. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Hooks can be overridden belaran@999: belaran@999: Mercurial allows you to override a hook definition by belaran@999: redefining the hook. You can disable it by setting its value belaran@999: to the empty string, or change its behavior as you wish. belaran@999: belaran@999: belaran@999: If you deploy a system- or site-wide ~/.hgrc file that defines some belaran@999: hooks, you should thus understand that your users can disable belaran@999: or override those hooks. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Ensuring that critical hooks are run belaran@999: belaran@999: Sometimes you may want to enforce a policy that you do not belaran@999: want others to be able to work around. For example, you may belaran@999: have a requirement that every changeset must pass a rigorous belaran@999: set of tests. Defining this requirement via a hook in a belaran@999: site-wide ~/.hgrc won't belaran@999: work for remote users on laptops, and of course local users belaran@999: can subvert it at will by overriding the hook. belaran@999: belaran@999: belaran@999: Instead, you can set up your policies for use of Mercurial belaran@999: so that people are expected to propagate changes through a belaran@999: well-known canonical server that you have belaran@999: locked down and configured appropriately. belaran@999: belaran@999: belaran@999: One way to do this is via a combination of social belaran@999: engineering and technology. Set up a restricted-access belaran@999: account; users can push changes over the network to belaran@999: repositories managed by this account, but they cannot log into belaran@999: the account and run normal shell commands. In this scenario, belaran@999: a user can commit a changeset that contains any old garbage belaran@999: they want. belaran@999: belaran@999: belaran@999: When someone pushes a changeset to the server that belaran@999: everyone pulls from, the server will test the changeset before belaran@999: it accepts it as permanent, and reject it if it fails to pass belaran@999: the test suite. If people only pull changes from this belaran@999: filtering server, it will serve to ensure that all changes belaran@999: that people pull have been automatically vetted. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: A short tutorial on using hooks belaran@999: belaran@999: It is easy to write a Mercurial hook. Let's start with a belaran@999: hook that runs when you finish a hg belaran@999: commit, and simply prints the hash of the changeset belaran@999: you just created. The hook is called commit. belaran@999: belaran@999: belaran@999: All hooks follow the pattern in this example. belaran@999: belaran@999: belaran@999: $ hg init hook-test belaran@999: $ cd hook-test belaran@999: $ echo '[hooks]' >> .hg/hgrc belaran@999: $ echo 'commit = echo committed $HG_NODE' >> .hg/hgrc belaran@999: $ cat .hg/hgrc belaran@999: [hooks] belaran@999: commit = echo committed $HG_NODE belaran@999: $ echo a > a belaran@999: $ hg add a belaran@999: $ hg commit -m 'testing commit hook' belaran@999: committed 13a334d1e5ca83fea465aa779110eec3c5ddd6b1 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You add an entry to the hooks section of your ~/.hgrc. On the left is the name of belaran@999: the event to trigger on; on the right is the action to take. As belaran@999: you can see, you can run an arbitrary shell command in a hook. belaran@999: Mercurial passes extra information to the hook using environment belaran@999: variables (look for HG_NODE in the example). belaran@999: belaran@999: belaran@999: belaran@999: Performing multiple actions per event belaran@999: belaran@999: Quite often, you will want to define more than one hook belaran@999: for a particular kind of event, as shown below. belaran@999: belaran@999: belaran@999: $ echo 'commit.when = echo -n "date of commit: "; date' >> .hg/hgrc belaran@999: $ echo a >> a belaran@999: $ hg commit -m 'i have two hooks' belaran@999: committed 3be6e2778fb853cbc7e5138d0b9c29386504670b belaran@999: date of commit: Sun Aug 16 14:05:05 GMT 2009 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial lets you do this by adding an belaran@999: extension to the end of a hook's name. belaran@999: You extend a hook's name by giving the name of the hook, belaran@999: followed by a full stop (the belaran@999: . character), followed by belaran@999: some more text of your choosing. For example, Mercurial will belaran@999: run both commit.foo and belaran@999: commit.bar when the belaran@999: commit event occurs. belaran@999: belaran@999: belaran@999: To give a well-defined order of execution when there are belaran@999: multiple hooks defined for an event, Mercurial sorts hooks by belaran@999: extension, and executes the hook commands in this sorted belaran@999: order. In the above example, it will execute belaran@999: commit.bar before belaran@999: commit.foo, and commit belaran@999: before both. belaran@999: belaran@999: belaran@999: It is a good idea to use a somewhat descriptive belaran@999: extension when you define a new hook. This will help you to belaran@999: remember what the hook was for. If the hook fails, you'll get belaran@999: an error message that contains the hook name and extension, so belaran@999: using a descriptive extension could give you an immediate hint belaran@999: as to why the hook failed (see for an example). belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Controlling whether an activity can proceed belaran@999: belaran@999: In our earlier examples, we used the commit hook, which is run after a belaran@999: commit has completed. This is one of several Mercurial hooks belaran@999: that run after an activity finishes. Such hooks have no way belaran@999: of influencing the activity itself. belaran@999: belaran@999: belaran@999: Mercurial defines a number of events that occur before an belaran@999: activity starts; or after it starts, but before it finishes. belaran@999: Hooks that trigger on these events have the added ability to belaran@999: choose whether the activity can continue, or will abort. belaran@999: belaran@999: belaran@999: The pretxncommit hook runs belaran@999: after a commit has all but completed. In other words, the belaran@999: metadata representing the changeset has been written out to belaran@999: disk, but the transaction has not yet been allowed to belaran@999: complete. The pretxncommit belaran@999: hook has the ability to decide whether the transaction can belaran@999: complete, or must be rolled back. belaran@999: belaran@999: belaran@999: If the pretxncommit hook belaran@999: exits with a status code of zero, the transaction is allowed belaran@999: to complete; the commit finishes; and the commit hook is run. If the pretxncommit hook exits with a belaran@999: non-zero status code, the transaction is rolled back; the belaran@999: metadata representing the changeset is erased; and the belaran@999: commit hook is not run. belaran@999: belaran@999: belaran@999: belaran@999: $ cat check_bug_id belaran@999: #!/bin/sh belaran@999: # check that a commit comment mentions a numeric bug id belaran@999: hg log -r $1 --template {desc} | grep -q "\<bug *[0-9]" belaran@999: $ echo 'pretxncommit.bug_id_required = ./check_bug_id $HG_NODE' >> .hg/hgrc belaran@999: $ echo a >> a belaran@999: $ hg commit -m 'i am not mentioning a bug id' belaran@999: transaction abort! belaran@999: rollback completed belaran@999: abort: pretxncommit.bug_id_required hook exited with status 1 belaran@999: $ hg commit -m 'i refer you to bug 666' belaran@999: committed 1a52be73a1ca4fa05e269f99003ed00912e8e836 belaran@999: date of commit: Sun Aug 16 14:05:05 GMT 2009 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The hook in the example above checks that a commit comment belaran@999: contains a bug ID. If it does, the commit can complete. If belaran@999: not, the commit is rolled back. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Writing your own hooks belaran@999: belaran@999: When you are writing a hook, you might find it useful to run belaran@999: Mercurial either with the option, or the verbose config item set to belaran@999: true. When you do so, Mercurial will print a belaran@999: message before it calls each hook. belaran@999: belaran@999: belaran@999: belaran@999: Choosing how your hook should run belaran@999: belaran@999: You can write a hook either as a normal belaran@999: program—typically a shell script—or as a Python belaran@999: function that is executed within the Mercurial process. belaran@999: belaran@999: belaran@999: Writing a hook as an external program has the advantage belaran@999: that it requires no knowledge of Mercurial's internals. You belaran@999: can call normal Mercurial commands to get any added belaran@999: information you need. The trade-off is that external hooks belaran@999: are slower than in-process hooks. belaran@999: belaran@999: belaran@999: An in-process Python hook has complete access to the belaran@999: Mercurial API, and does not shell out to belaran@999: another process, so it is inherently faster than an external belaran@999: hook. It is also easier to obtain much of the information belaran@999: that a hook requires by using the Mercurial API than by belaran@999: running Mercurial commands. belaran@999: belaran@999: belaran@999: If you are comfortable with Python, or require high belaran@999: performance, writing your hooks in Python may be a good belaran@999: choice. However, when you have a straightforward hook to belaran@999: write and you don't need to care about performance (probably belaran@999: the majority of hooks), a shell script is perfectly fine. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Hook parameters belaran@999: belaran@999: Mercurial calls each hook with a set of well-defined belaran@999: parameters. In Python, a parameter is passed as a keyword belaran@999: argument to your hook function. For an external program, a belaran@999: parameter is passed as an environment variable. belaran@999: belaran@999: belaran@999: Whether your hook is written in Python or as a shell belaran@999: script, the hook-specific parameter names and values will be belaran@999: the same. A boolean parameter will be represented as a belaran@999: boolean value in Python, but as the number 1 (for belaran@999: true) or 0 (for false) as an belaran@999: environment variable for an external hook. If a hook belaran@999: parameter is named foo, the keyword belaran@999: argument for a Python hook will also be named belaran@999: foo, while the environment variable for an belaran@999: external hook will be named HG_FOO. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Hook return values and activity control belaran@999: belaran@999: A hook that executes successfully must exit with a status belaran@999: of zero if external, or return boolean false if belaran@999: in-process. Failure is indicated with a non-zero exit status belaran@999: from an external hook, or an in-process hook returning boolean belaran@999: true. If an in-process hook raises an belaran@999: exception, the hook is considered to have failed. belaran@999: belaran@999: belaran@999: For a hook that controls whether an activity can proceed, belaran@999: zero/false means allow, while belaran@999: non-zero/true/exception means deny. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Writing an external hook belaran@999: belaran@999: When you define an external hook in your ~/.hgrc and the hook is run, its belaran@999: value is passed to your shell, which interprets it. This belaran@999: means that you can use normal shell constructs in the body of belaran@999: the hook. belaran@999: belaran@999: belaran@999: An executable hook is always run with its current belaran@999: directory set to a repository's root directory. belaran@999: belaran@999: belaran@999: Each hook parameter is passed in as an environment belaran@999: variable; the name is upper-cased, and prefixed with the belaran@999: string HG_. belaran@999: belaran@999: belaran@999: With the exception of hook parameters, Mercurial does not belaran@999: set or modify any environment variables when running a hook. belaran@999: This is useful to remember if you are writing a site-wide hook belaran@999: that may be run by a number of different users with differing belaran@999: environment variables set. In multi-user situations, you belaran@999: should not rely on environment variables being set to the belaran@999: values you have in your environment when testing the hook. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Telling Mercurial to use an in-process hook belaran@999: belaran@999: The ~/.hgrc syntax belaran@999: for defining an in-process hook is slightly different than for belaran@999: an executable hook. The value of the hook must start with the belaran@999: text python:, and continue belaran@999: with the fully-qualified name of a callable object to use as belaran@999: the hook's value. belaran@999: belaran@999: belaran@999: The module in which a hook lives is automatically imported belaran@999: when a hook is run. So long as you have the module name and belaran@999: PYTHONPATH right, it should just belaran@999: work. belaran@999: belaran@999: belaran@999: The following ~/.hgrc belaran@999: example snippet illustrates the syntax and meaning of the belaran@999: notions we just described. belaran@999: belaran@999: [hooks] belaran@999: commit.example = python:mymodule.submodule.myhook belaran@999: When Mercurial runs the commit.example belaran@999: hook, it imports mymodule.submodule, looks belaran@999: for the callable object named myhook, and belaran@999: calls it. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Writing an in-process hook belaran@999: belaran@999: The simplest in-process hook does nothing, but illustrates belaran@999: the basic shape of the hook API: belaran@999: belaran@999: def myhook(ui, repo, **kwargs): belaran@999: pass belaran@999: The first argument to a Python hook is always a ui object. The second belaran@999: is a repository object; at the moment, it is always an belaran@999: instance of localrepository. belaran@999: Following these two arguments are other keyword arguments. belaran@999: Which ones are passed in depends on the hook being called, but belaran@999: a hook can ignore arguments it doesn't care about by dropping belaran@999: them into a keyword argument dict, as with belaran@999: **kwargs above. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Some hook examples belaran@999: belaran@999: belaran@999: Writing meaningful commit messages belaran@999: belaran@999: It's hard to imagine a useful commit message being very belaran@999: short. The simple pretxncommit belaran@999: hook of the example below will prevent you from committing a belaran@999: changeset with a message that is less than ten bytes long. belaran@999: belaran@999: belaran@999: belaran@999: $ cat .hg/hgrc belaran@999: [hooks] belaran@999: pretxncommit.msglen = test `hg tip --template {desc} | wc -c` -ge 10 belaran@999: $ echo a > a belaran@999: $ hg add a belaran@999: $ hg commit -A -m 'too short' belaran@999: transaction abort! belaran@999: rollback completed belaran@999: abort: pretxncommit.msglen hook exited with status 1 belaran@999: $ hg commit -A -m 'long enough' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Checking for trailing whitespace belaran@999: belaran@999: An interesting use of a commit-related hook is to help you belaran@999: to write cleaner code. A simple example of cleaner belaran@999: code is the dictum that a change should not add any belaran@999: new lines of text that contain trailing belaran@999: whitespace. Trailing whitespace is a series of belaran@999: space and tab characters at the end of a line of text. In belaran@999: most cases, trailing whitespace is unnecessary, invisible belaran@999: noise, but it is occasionally problematic, and people often belaran@999: prefer to get rid of it. belaran@999: belaran@999: belaran@999: You can use either the precommit or pretxncommit hook to tell whether you belaran@999: have a trailing whitespace problem. If you use the precommit hook, the hook will not know belaran@999: which files you are committing, so it will have to check every belaran@999: modified file in the repository for trailing white space. If belaran@999: you want to commit a change to just the file belaran@999: foo, but the file belaran@999: bar contains trailing whitespace, doing a belaran@999: check in the precommit hook belaran@999: will prevent you from committing foo due belaran@999: to the problem with bar. This doesn't belaran@999: seem right. belaran@999: belaran@999: belaran@999: Should you choose the pretxncommit hook, the check won't belaran@999: occur until just before the transaction for the commit belaran@999: completes. This will allow you to check for problems only the belaran@999: exact files that are being committed. However, if you entered belaran@999: the commit message interactively and the hook fails, the belaran@999: transaction will roll back; you'll have to re-enter the commit belaran@999: message after you fix the trailing whitespace and run hg commit again. belaran@999: belaran@999: belaran@999: belaran@999: $ cat .hg/hgrc belaran@999: [hooks] belaran@999: pretxncommit.whitespace = hg export tip | (! egrep -q '^\+.*[ \t]$') belaran@999: $ echo 'a ' > a belaran@999: $ hg commit -A -m 'test with trailing whitespace' belaran@999: adding a belaran@999: transaction abort! belaran@999: rollback completed belaran@999: abort: pretxncommit.whitespace hook exited with status 1 belaran@999: $ echo 'a' > a belaran@999: $ hg commit -A -m 'drop trailing whitespace and try again' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: In this example, we introduce a simple pretxncommit hook that checks for belaran@999: trailing whitespace. This hook is short, but not very belaran@999: helpful. It exits with an error status if a change adds a belaran@999: line with trailing whitespace to any file, but does not print belaran@999: any information that might help us to identify the offending belaran@999: file or line. It also has the nice property of not paying belaran@999: attention to unmodified lines; only lines that introduce new belaran@999: trailing whitespace cause problems. belaran@999: belaran@999: belaran@999: belaran@999: #!/usr/bin/env python belaran@999: # belaran@999: # save as .hg/check_whitespace.py and make executable belaran@999: belaran@999: import re belaran@999: belaran@999: def trailing_whitespace(difflines): belaran@999: # belaran@999: linenum, header = 0, False belaran@999: belaran@999: for line in difflines: belaran@999: if header: belaran@999: # remember the name of the file that this diff affects belaran@999: m = re.match(r'(?:---|\+\+\+) ([^\t]+)', line) belaran@999: if m and m.group(1) != '/dev/null': belaran@999: filename = m.group(1).split('/', 1)[-1] belaran@999: if line.startswith('+++ '): belaran@999: header = False belaran@999: continue belaran@999: if line.startswith('diff '): belaran@999: header = True belaran@999: continue belaran@999: # hunk header - save the line number belaran@999: m = re.match(r'@@ -\d+,\d+ \+(\d+),', line) belaran@999: if m: belaran@999: linenum = int(m.group(1)) belaran@999: continue belaran@999: # hunk body - check for an added line with trailing whitespace belaran@999: m = re.match(r'\+.*\s$', line) belaran@999: if m: belaran@999: yield filename, linenum belaran@999: if line and line[0] in ' +': belaran@999: linenum += 1 belaran@999: belaran@999: if __name__ == '__main__': belaran@999: import os, sys belaran@999: belaran@999: added = 0 belaran@999: for filename, linenum in trailing_whitespace(os.popen('hg export tip')): belaran@999: print >> sys.stderr, ('%s, line %d: trailing whitespace added' % belaran@999: (filename, linenum)) belaran@999: added += 1 belaran@999: if added: belaran@999: # save the commit message so we don't need to retype it belaran@999: os.system('hg tip --template "{desc}" > .hg/commit.save') belaran@999: print >> sys.stderr, 'commit message saved to .hg/commit.save' belaran@999: sys.exit(1) belaran@999: belaran@999: belaran@999: belaran@999: The above version is much more complex, but also more belaran@999: useful. It parses a unified diff to see if any lines add belaran@999: trailing whitespace, and prints the name of the file and the belaran@999: line number of each such occurrence. Even better, if the belaran@999: change adds trailing whitespace, this hook saves the commit belaran@999: comment and prints the name of the save file before exiting belaran@999: and telling Mercurial to roll the transaction back, so you can belaran@999: use the belaran@999: option to hg commit to reuse belaran@999: the saved commit message once you've corrected the problem. belaran@999: belaran@999: belaran@999: belaran@999: $ cat .hg/hgrc belaran@999: [hooks] belaran@999: pretxncommit.whitespace = .hg/check_whitespace.py belaran@999: $ echo 'a ' >> a belaran@999: $ hg commit -A -m 'add new line with trailing whitespace' belaran@999: a, line 2: trailing whitespace added belaran@999: commit message saved to .hg/commit.save belaran@999: transaction abort! belaran@999: rollback completed belaran@999: abort: pretxncommit.whitespace hook exited with status 1 belaran@999: $ sed -i 's, *$,,' a belaran@999: $ hg commit -A -m 'trimmed trailing whitespace' belaran@999: a, line 2: trailing whitespace added belaran@999: commit message saved to .hg/commit.save belaran@999: transaction abort! belaran@999: rollback completed belaran@999: abort: pretxncommit.whitespace hook exited with status 1 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: As a final aside, note in the example above the belaran@999: use of sed's in-place editing feature to belaran@999: get rid of trailing whitespace from a file. This is concise belaran@999: and useful enough that I will reproduce it here (using belaran@999: perl for good measure). belaran@999: perl -pi -e 's,\s+$,,' filename belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Bundled hooks belaran@999: belaran@999: Mercurial ships with several bundled hooks. You can find belaran@999: them in the hgext belaran@999: directory of a Mercurial source tree. If you are using a belaran@999: Mercurial binary package, the hooks will be located in the belaran@999: hgext directory of belaran@999: wherever your package installer put Mercurial. belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hg-ext" moreinfo="none">acl</literal>—access belaran@999: control for parts of a repository belaran@999: belaran@999: The acl extension lets belaran@999: you control which remote users are allowed to push changesets belaran@999: to a networked server. You can protect any portion of a belaran@999: repository (including the entire repo), so that a specific belaran@999: remote user can push changes that do not affect the protected belaran@999: portion. belaran@999: belaran@999: belaran@999: This extension implements access control based on the belaran@999: identity of the user performing a push, belaran@999: not on who committed the changesets belaran@999: they're pushing. It makes sense to use this hook only if you belaran@999: have a locked-down server environment that authenticates belaran@999: remote users, and you want to be sure that only specific users belaran@999: are allowed to push changes to that server. belaran@999: belaran@999: belaran@999: belaran@999: Configuring the <literal role="hook" moreinfo="none">acl</literal> belaran@999: hook belaran@999: belaran@999: In order to manage incoming changesets, the acl hook must be used as a belaran@999: pretxnchangegroup hook. This belaran@999: lets it see which files are modified by each incoming belaran@999: changeset, and roll back a group of changesets if they belaran@999: modify forbidden files. Example: belaran@999: belaran@999: [hooks] belaran@999: pretxnchangegroup.acl = python:hgext.acl.hook belaran@999: belaran@999: The acl extension is belaran@999: configured using three sections. belaran@999: belaran@999: belaran@999: The acl section has belaran@999: only one entry, sources, belaran@999: which lists the sources of incoming changesets that the hook belaran@999: should pay attention to. You don't normally need to belaran@999: configure this section. belaran@999: belaran@999: belaran@999: serve: belaran@999: Control incoming changesets that are arriving from a belaran@999: remote repository over http or ssh. This is the default belaran@999: value of sources, and belaran@999: usually the only setting you'll need for this belaran@999: configuration item. belaran@999: belaran@999: belaran@999: pull: belaran@999: Control incoming changesets that are arriving via a pull belaran@999: from a local repository. belaran@999: belaran@999: belaran@999: push: belaran@999: Control incoming changesets that are arriving via a push belaran@999: from a local repository. belaran@999: belaran@999: belaran@999: bundle: belaran@999: Control incoming changesets that are arriving from belaran@999: another repository via a bundle. belaran@999: belaran@999: belaran@999: belaran@999: The acl.allow belaran@999: section controls the users that are allowed to add belaran@999: changesets to the repository. If this section is not belaran@999: present, all users that are not explicitly denied are belaran@999: allowed. If this section is present, all users that are not belaran@999: explicitly allowed are denied (so an empty section means belaran@999: that all users are denied). belaran@999: belaran@999: belaran@999: The acl.deny belaran@999: section determines which users are denied from adding belaran@999: changesets to the repository. If this section is not belaran@999: present or is empty, no users are denied. belaran@999: belaran@999: belaran@999: The syntaxes for the acl.allow and acl.deny sections are belaran@999: identical. On the left of each entry is a glob pattern that belaran@999: matches files or directories, relative to the root of the belaran@999: repository; on the right, a user name. belaran@999: belaran@999: belaran@999: In the following example, the user belaran@999: docwriter can only push changes to the belaran@999: docs subtree of the belaran@999: repository, while intern can push changes belaran@999: to any file or directory except source/sensitive. belaran@999: belaran@999: [acl.allow] belaran@999: docs/** = docwriter belaran@999: [acl.deny] belaran@999: source/sensitive/** = intern belaran@999: belaran@999: belaran@999: belaran@999: Testing and troubleshooting belaran@999: belaran@999: If you want to test the acl hook, run it with Mercurial's belaran@999: debugging output enabled. Since you'll probably be running belaran@999: it on a server where it's not convenient (or sometimes belaran@999: possible) to pass in the option, don't forget belaran@999: that you can enable debugging output in your ~/.hgrc: belaran@999: belaran@999: [ui] belaran@999: debug = true belaran@999: With this enabled, the acl hook will print enough belaran@999: information to let you figure out why it is allowing or belaran@999: forbidding pushes from specific users. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hg-ext" moreinfo="none">bugzilla</literal>—integration with belaran@999: Bugzilla belaran@999: belaran@999: The bugzilla extension belaran@999: adds a comment to a Bugzilla bug whenever it finds a reference belaran@999: to that bug ID in a commit comment. You can install this hook belaran@999: on a shared server, so that any time a remote user pushes belaran@999: changes to this server, the hook gets run. belaran@999: belaran@999: belaran@999: It adds a comment to the bug that looks like this (you can belaran@999: configure the contents of the comment—see below): belaran@999: belaran@999: Changeset aad8b264143a, made by Joe User belaran@999: <joe.user@domain.com> in the frobnitz repository, refers belaran@999: to this bug. For complete details, see belaran@999: http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a belaran@999: Changeset description: Fix bug 10483 by guarding against some belaran@999: NULL pointers belaran@999: The value of this hook is that it automates the process of belaran@999: updating a bug any time a changeset refers to it. If you belaran@999: configure the hook properly, it makes it easy for people to belaran@999: browse straight from a Bugzilla bug to a changeset that refers belaran@999: to that bug. belaran@999: belaran@999: belaran@999: You can use the code in this hook as a starting point for belaran@999: some more exotic Bugzilla integration recipes. Here are a few belaran@999: possibilities: belaran@999: belaran@999: belaran@999: Require that every changeset pushed to the belaran@999: server have a valid bug ID in its commit comment. In this belaran@999: case, you'd want to configure the hook as a pretxncommit hook. This would belaran@999: allow the hook to reject changes that didn't contain bug belaran@999: IDs. belaran@999: belaran@999: belaran@999: Allow incoming changesets to automatically belaran@999: modify the state of a bug, as well as belaran@999: simply adding a comment. For example, the hook could belaran@999: recognise the string fixed bug 31337 as belaran@999: indicating that it should update the state of bug 31337 to belaran@999: requires testing. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Configuring the <literal role="hook" moreinfo="none">bugzilla</literal> belaran@999: hook belaran@999: belaran@999: You should configure this hook in your server's belaran@999: ~/.hgrc as an incoming hook, for example as belaran@999: follows: belaran@999: belaran@999: [hooks] belaran@999: incoming.bugzilla = python:hgext.bugzilla.hook belaran@999: belaran@999: Because of the specialised nature of this hook, and belaran@999: because Bugzilla was not written with this kind of belaran@999: integration in mind, configuring this hook is a somewhat belaran@999: involved process. belaran@999: belaran@999: belaran@999: Before you begin, you must install the MySQL bindings belaran@999: for Python on the host(s) where you'll be running the hook. belaran@999: If this is not available as a binary package for your belaran@999: system, you can download it from belaran@999: web:mysql-python. belaran@999: belaran@999: belaran@999: Configuration information for this hook lives in the belaran@999: bugzilla section of belaran@999: your ~/.hgrc. belaran@999: belaran@999: belaran@999: version: The version belaran@999: of Bugzilla installed on the server. The database belaran@999: schema that Bugzilla uses changes occasionally, so this belaran@999: hook has to know exactly which schema to use. belaran@999: belaran@999: host: belaran@999: The hostname of the MySQL server that stores your belaran@999: Bugzilla data. The database must be configured to allow belaran@999: connections from whatever host you are running the belaran@999: bugzilla hook on. belaran@999: belaran@999: belaran@999: user: belaran@999: The username with which to connect to the MySQL server. belaran@999: The database must be configured to allow this user to belaran@999: connect from whatever host you are running the bugzilla hook on. This user belaran@999: must be able to access and modify Bugzilla tables. The belaran@999: default value of this item is bugs, belaran@999: which is the standard name of the Bugzilla user in a belaran@999: MySQL database. belaran@999: belaran@999: belaran@999: password: The MySQL belaran@999: password for the user you configured above. This is belaran@999: stored as plain text, so you should make sure that belaran@999: unauthorised users cannot read the ~/.hgrc file where you belaran@999: store this information. belaran@999: belaran@999: belaran@999: db: belaran@999: The name of the Bugzilla database on the MySQL server. belaran@999: The default value of this item is belaran@999: bugs, which is the standard name of belaran@999: the MySQL database where Bugzilla stores its data. belaran@999: belaran@999: belaran@999: notify: If you want belaran@999: Bugzilla to send out a notification email to subscribers belaran@999: after this hook has added a comment to a bug, you will belaran@999: need this hook to run a command whenever it updates the belaran@999: database. The command to run depends on where you have belaran@999: installed Bugzilla, but it will typically look something belaran@999: like this, if you have Bugzilla installed in /var/www/html/bugzilla: belaran@999: belaran@999: cd /var/www/html/bugzilla && belaran@999: ./processmail %s nobody@nowhere.com belaran@999: belaran@999: The Bugzilla belaran@999: processmail program expects to be belaran@999: given a bug ID (the hook replaces belaran@999: %s with the bug ID) belaran@999: and an email address. It also expects to be able to belaran@999: write to some files in the directory that it runs in. belaran@999: If Bugzilla and this hook are not installed on the same belaran@999: machine, you will need to find a way to run belaran@999: processmail on the server where belaran@999: Bugzilla is installed. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mapping committer names to Bugzilla user names belaran@999: belaran@999: By default, the bugzilla hook tries to use the belaran@999: email address of a changeset's committer as the Bugzilla belaran@999: user name with which to update a bug. If this does not suit belaran@999: your needs, you can map committer email addresses to belaran@999: Bugzilla user names using a usermap section. belaran@999: belaran@999: belaran@999: Each item in the usermap section contains an belaran@999: email address on the left, and a Bugzilla user name on the belaran@999: right. belaran@999: belaran@999: [usermap] belaran@999: jane.user@example.com = jane belaran@999: You can either keep the usermap data in a normal belaran@999: ~/.hgrc, or tell the belaran@999: bugzilla hook to read the belaran@999: information from an external usermap belaran@999: file. In the latter case, you can store belaran@999: usermap data by itself in (for example) belaran@999: a user-modifiable repository. This makes it possible to let belaran@999: your users maintain their own usermap entries. The main belaran@999: ~/.hgrc file might look belaran@999: like this: belaran@999: belaran@999: # regular hgrc file refers to external usermap file belaran@999: [bugzilla] belaran@999: usermap = /home/hg/repos/userdata/bugzilla-usermap.conf belaran@999: While the usermap file that it belaran@999: refers to might look like this: belaran@999: belaran@999: # bugzilla-usermap.conf - inside a hg repository belaran@999: [usermap] stephanie@example.com = steph belaran@999: belaran@999: belaran@999: belaran@999: Configuring the text that gets added to a bug belaran@999: belaran@999: You can configure the text that this hook adds as a belaran@999: comment; you specify it in the form of a Mercurial template. belaran@999: Several ~/.hgrc entries belaran@999: (still in the bugzilla belaran@999: section) control this behavior. belaran@999: belaran@999: belaran@999: strip: The number of belaran@999: leading path elements to strip from a repository's path belaran@999: name to construct a partial path for a URL. For example, belaran@999: if the repositories on your server live under /home/hg/repos, and you belaran@999: have a repository whose path is /home/hg/repos/app/tests, belaran@999: then setting strip to belaran@999: 4 will give a partial path of belaran@999: app/tests. The belaran@999: hook will make this partial path available when belaran@999: expanding a template, as webroot. belaran@999: belaran@999: belaran@999: template: The text of the belaran@999: template to use. In addition to the usual belaran@999: changeset-related variables, this template can use belaran@999: hgweb (the value of the belaran@999: hgweb configuration item above) and belaran@999: webroot (the path constructed using belaran@999: strip above). belaran@999: belaran@999: belaran@999: belaran@999: In addition, you can add a baseurl item to the web section of your ~/.hgrc. The bugzilla hook will make this belaran@999: available when expanding a template, as the base string to belaran@999: use when constructing a URL that will let users browse from belaran@999: a Bugzilla comment to view a changeset. Example: belaran@999: belaran@999: [web] belaran@999: baseurl = http://hg.domain.com/ belaran@999: belaran@999: Here is an example set of bugzilla hook config information. belaran@999: belaran@999: belaran@999: belaran@999: [bugzilla] belaran@999: host = bugzilla.example.com belaran@999: password = mypassword version = 2.16 belaran@999: # server-side repos live in /home/hg/repos, so strip 4 leading belaran@999: # separators belaran@999: strip = 4 belaran@999: hgweb = http://hg.example.com/ belaran@999: usermap = /home/hg/repos/notify/bugzilla.conf belaran@999: template = Changeset {node|short}, made by {author} in the {webroot} belaran@999: repo, refers to this bug.\n belaran@999: For complete details, see belaran@999: {hgweb}{webroot}?cmd=changeset;node={node|short}\n belaran@999: Changeset description:\n belaran@999: \t{desc|tabindent} belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Testing and troubleshooting belaran@999: belaran@999: The most common problems with configuring the bugzilla hook relate to running belaran@999: Bugzilla's processmail script and belaran@999: mapping committer names to user names. belaran@999: belaran@999: belaran@999: Recall from above that the user belaran@999: that runs the Mercurial process on the server is also the belaran@999: one that will run the processmail belaran@999: script. The processmail script belaran@999: sometimes causes Bugzilla to write to files in its belaran@999: configuration directory, and Bugzilla's configuration files belaran@999: are usually owned by the user that your web server runs belaran@999: under. belaran@999: belaran@999: belaran@999: You can cause processmail to be run belaran@999: with the suitable user's identity using the belaran@999: sudo command. Here is an example entry belaran@999: for a sudoers file. belaran@999: belaran@999: hg_user = (httpd_user) belaran@999: NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s belaran@999: This allows the hg_user user to run a belaran@999: processmail-wrapper program under the belaran@999: identity of httpd_user. belaran@999: belaran@999: belaran@999: This indirection through a wrapper script is necessary, belaran@999: because processmail expects to be run belaran@999: with its current directory set to wherever you installed belaran@999: Bugzilla; you can't specify that kind of constraint in a belaran@999: sudoers file. The contents of the belaran@999: wrapper script are simple: belaran@999: belaran@999: #!/bin/sh belaran@999: cd `dirname $0` && ./processmail "$1" nobody@example.com belaran@999: It doesn't seem to matter what email address you pass to belaran@999: processmail. belaran@999: belaran@999: belaran@999: If your usermap is belaran@999: not set up correctly, users will see an error message from belaran@999: the bugzilla hook when they belaran@999: push changes to the server. The error message will look belaran@999: like this: belaran@999: belaran@999: cannot find bugzilla user id for john.q.public@example.com belaran@999: What this means is that the committer's address, belaran@999: john.q.public@example.com, is not a valid belaran@999: Bugzilla user name, nor does it have an entry in your belaran@999: usermap that maps it to belaran@999: a valid Bugzilla user name. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hg-ext" moreinfo="none">notify</literal>—send email belaran@999: notifications belaran@999: belaran@999: Although Mercurial's built-in web server provides RSS belaran@999: feeds of changes in every repository, many people prefer to belaran@999: receive change notifications via email. The notify hook lets you send out belaran@999: notifications to a set of email addresses whenever changesets belaran@999: arrive that those subscribers are interested in. belaran@999: belaran@999: belaran@999: As with the bugzilla belaran@999: hook, the notify hook is belaran@999: template-driven, so you can customise the contents of the belaran@999: notification messages that it sends. belaran@999: belaran@999: belaran@999: By default, the notify belaran@999: hook includes a diff of every changeset that it sends out; you belaran@999: can limit the size of the diff, or turn this feature off belaran@999: entirely. It is useful for letting subscribers review changes belaran@999: immediately, rather than clicking to follow a URL. belaran@999: belaran@999: belaran@999: belaran@999: Configuring the <literal role="hg-ext" moreinfo="none">notify</literal> belaran@999: hook belaran@999: belaran@999: You can set up the notify hook to send one email belaran@999: message per incoming changeset, or one per incoming group of belaran@999: changesets (all those that arrived in a single pull or belaran@999: push). belaran@999: belaran@999: [hooks] belaran@999: # send one email per group of changes belaran@999: changegroup.notify = python:hgext.notify.hook belaran@999: # send one email per change belaran@999: incoming.notify = python:hgext.notify.hook belaran@999: belaran@999: Configuration information for this hook lives in the belaran@999: notify section of a belaran@999: ~/.hgrc file. belaran@999: belaran@999: belaran@999: test: belaran@999: By default, this hook does not send out email at all; belaran@999: instead, it prints the message that it belaran@999: would send. Set this item to belaran@999: false to allow email to be sent. The belaran@999: reason that sending of email is turned off by default is belaran@999: that it takes several tries to configure this extension belaran@999: exactly as you would like, and it would be bad form to belaran@999: spam subscribers with a number of broken belaran@999: notifications while you debug your configuration. belaran@999: belaran@999: belaran@999: config: belaran@999: The path to a configuration file that contains belaran@999: subscription information. This is kept separate from belaran@999: the main ~/.hgrc so belaran@999: that you can maintain it in a repository of its own. belaran@999: People can then clone that repository, update their belaran@999: subscriptions, and push the changes back to your server. belaran@999: belaran@999: belaran@999: strip: belaran@999: The number of leading path separator characters to strip belaran@999: from a repository's path, when deciding whether a belaran@999: repository has subscribers. For example, if the belaran@999: repositories on your server live in /home/hg/repos, and belaran@999: notify is considering a belaran@999: repository named /home/hg/repos/shared/test, belaran@999: setting strip to belaran@999: 4 will cause notify to trim the path it belaran@999: considers down to shared/test, and it will belaran@999: match subscribers against that. belaran@999: belaran@999: belaran@999: template: The template belaran@999: text to use when sending messages. This specifies both belaran@999: the contents of the message header and its body. belaran@999: belaran@999: belaran@999: maxdiff: The maximum belaran@999: number of lines of diff data to append to the end of a belaran@999: message. If a diff is longer than this, it is belaran@999: truncated. By default, this is set to 300. Set this to belaran@999: 0 to omit diffs from notification belaran@999: emails. belaran@999: belaran@999: belaran@999: sources: A list of belaran@999: sources of changesets to consider. This lets you limit belaran@999: notify to only sending belaran@999: out email about changes that remote users pushed into belaran@999: this repository via a server, for example. See belaran@999: for the sources you belaran@999: can specify here. belaran@999: belaran@999: belaran@999: belaran@999: If you set the baseurl belaran@999: item in the web section, belaran@999: you can use it in a template; it will be available as belaran@999: webroot. belaran@999: belaran@999: belaran@999: Here is an example set of notify configuration information. belaran@999: belaran@999: belaran@999: belaran@999: [notify] belaran@999: # really send email belaran@999: test = false belaran@999: # subscriber data lives in the notify repo belaran@999: config = /home/hg/repos/notify/notify.conf belaran@999: # repos live in /home/hg/repos on server, so strip 4 "/" chars belaran@999: strip = 4 belaran@999: template = X-Hg-Repo: {webroot}\n belaran@999: Subject: {webroot}: {desc|firstline|strip}\n belaran@999: From: {author} belaran@999: \n\n belaran@999: changeset {node|short} in {root} belaran@999: \n\ndetails: belaran@999: {baseurl}{webroot}?cmd=changeset;node={node|short} belaran@999: description: {desc|tabindent|strip} belaran@999: belaran@999: [web] belaran@999: baseurl = belaran@999: http://hg.example.com/ belaran@999: belaran@999: belaran@999: belaran@999: This will produce a message that looks like the belaran@999: following: belaran@999: belaran@999: belaran@999: belaran@999: X-Hg-Repo: tests/slave belaran@999: Subject: tests/slave: Handle error case when slave has no buffers belaran@999: Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT) belaran@999: belaran@999: changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave belaran@999: belaran@999: details: belaran@999: http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b5 belaran@999: belaran@999: description: Handle error case when slave has no buffers belaran@999: belaran@999: diffs (54 lines): belaran@999: diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h belaran@999: --- a/include/tests.h Wed Aug 02 15:19:52 2006 -0700 belaran@999: +++ b/include/tests.h Wed Aug 02 15:25:26 2006 -0700 belaran@999: @@ -212,6 +212,15 @@ static __inline__ belaran@999: void test_headers(void *h) belaran@999: [...snip...] belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Testing and troubleshooting belaran@999: belaran@999: Do not forget that by default, the notify extension will not belaran@999: send any mail until you explicitly configure it to do so, belaran@999: by setting test to belaran@999: false. Until you do that, it simply belaran@999: prints the message it would send. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Information for writers of hooks belaran@999: belaran@999: belaran@999: In-process hook execution belaran@999: belaran@999: An in-process hook is called with arguments of the belaran@999: following form: belaran@999: belaran@999: def myhook(ui, repo, **kwargs): pass belaran@999: The ui parameter is a ui object. The belaran@999: repo parameter is a localrepository belaran@999: object. The names and values of the belaran@999: **kwargs parameters depend on the hook belaran@999: being invoked, with the following common features: belaran@999: belaran@999: belaran@999: If a parameter is named belaran@999: node or parentN, it belaran@999: will contain a hexadecimal changeset ID. The empty string belaran@999: is used to represent null changeset ID belaran@999: instead of a string of zeroes. belaran@999: belaran@999: belaran@999: If a parameter is named belaran@999: url, it will contain the URL of a belaran@999: remote repository, if that can be determined. belaran@999: belaran@999: belaran@999: Boolean-valued parameters are represented as belaran@999: Python bool objects. belaran@999: belaran@999: belaran@999: belaran@999: An in-process hook is called without a change to the belaran@999: process's working directory (unlike external hooks, which are belaran@999: run in the root of the repository). It must not change the belaran@999: process's working directory, or it will cause any calls it belaran@999: makes into the Mercurial API to fail. belaran@999: belaran@999: belaran@999: If a hook returns a boolean false value, it belaran@999: is considered to have succeeded. If it returns a boolean belaran@999: true value or raises an exception, it is belaran@999: considered to have failed. A useful way to think of the belaran@999: calling convention is tell me if you fail. belaran@999: belaran@999: belaran@999: Note that changeset IDs are passed into Python hooks as belaran@999: hexadecimal strings, not the binary hashes that Mercurial's belaran@999: APIs normally use. To convert a hash from hex to binary, use belaran@999: the bin function. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: External hook execution belaran@999: belaran@999: An external hook is passed to the shell of the user belaran@999: running Mercurial. Features of that shell, such as variable belaran@999: substitution and command redirection, are available. The hook belaran@999: is run in the root directory of the repository (unlike belaran@999: in-process hooks, which are run in the same directory that belaran@999: Mercurial was run in). belaran@999: belaran@999: belaran@999: Hook parameters are passed to the hook as environment belaran@999: variables. Each environment variable's name is converted in belaran@999: upper case and prefixed with the string belaran@999: HG_. For example, if the belaran@999: name of a parameter is node, belaran@999: the name of the environment variable representing that belaran@999: parameter will be HG_NODE. belaran@999: belaran@999: belaran@999: A boolean parameter is represented as the string belaran@999: 1 for true, belaran@999: 0 for false. belaran@999: If an environment variable is named HG_NODE, belaran@999: HG_PARENT1 or HG_PARENT2, it belaran@999: contains a changeset ID represented as a hexadecimal string. belaran@999: The empty string is used to represent null changeset belaran@999: ID instead of a string of zeroes. If an environment belaran@999: variable is named HG_URL, it will contain the belaran@999: URL of a remote repository, if that can be determined. belaran@999: belaran@999: belaran@999: If a hook exits with a status of zero, it is considered to belaran@999: have succeeded. If it exits with a non-zero status, it is belaran@999: considered to have failed. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Finding out where changesets come from belaran@999: belaran@999: A hook that involves the transfer of changesets between a belaran@999: local repository and another may be able to find out belaran@999: information about the far side. Mercurial belaran@999: knows how changes are being transferred, belaran@999: and in many cases where they are being belaran@999: transferred to or from. belaran@999: belaran@999: belaran@999: belaran@999: Sources of changesets belaran@999: belaran@999: Mercurial will tell a hook what means are, or were, used belaran@999: to transfer changesets between repositories. This is belaran@999: provided by Mercurial in a Python parameter named belaran@999: source, or an environment variable named belaran@999: HG_SOURCE. belaran@999: belaran@999: belaran@999: belaran@999: serve: Changesets are belaran@999: transferred to or from a remote repository over http or belaran@999: ssh. belaran@999: belaran@999: belaran@999: pull: Changesets are belaran@999: being transferred via a pull from one repository into belaran@999: another. belaran@999: belaran@999: belaran@999: push: Changesets are belaran@999: being transferred via a push from one repository into belaran@999: another. belaran@999: belaran@999: belaran@999: bundle: Changesets are belaran@999: being transferred to or from a bundle. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Where changes are going—remote repository belaran@999: URLs belaran@999: belaran@999: When possible, Mercurial will tell a hook the location belaran@999: of the far side of an activity that transfers belaran@999: changeset data between repositories. This is provided by belaran@999: Mercurial in a Python parameter named belaran@999: url, or an environment variable named belaran@999: HG_URL. belaran@999: belaran@999: belaran@999: This information is not always known. If a hook is belaran@999: invoked in a repository that is being served via http or belaran@999: ssh, Mercurial cannot tell where the remote repository is, belaran@999: but it may know where the client is connecting from. In belaran@999: such cases, the URL will take one of the following forms: belaran@999: belaran@999: belaran@999: remote:ssh:1.2.3.4—remote belaran@999: ssh client, at the IP address belaran@999: 1.2.3.4. belaran@999: belaran@999: belaran@999: remote:http:1.2.3.4—remote belaran@999: http client, at the IP address belaran@999: 1.2.3.4. If the client is using SSL, belaran@999: this will be of the form belaran@999: remote:https:1.2.3.4. belaran@999: belaran@999: belaran@999: Empty—no information could be belaran@999: discovered about the remote client. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Hook reference belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">changegroup</literal>—after belaran@999: remote changesets added belaran@999: belaran@999: This hook is run after a group of pre-existing changesets belaran@999: has been added to the repository, for example via a hg pull or hg belaran@999: unbundle. This hook is run once per operation belaran@999: that added one or more changesets. This is in contrast to the belaran@999: incoming hook, which is run belaran@999: once per changeset, regardless of whether the changesets belaran@999: arrive in a group. belaran@999: belaran@999: belaran@999: Some possible uses for this hook include kicking off an belaran@999: automated build or test of the added changesets, updating a belaran@999: bug database, or notifying subscribers that a repository belaran@999: contains new changes. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: changeset ID of the first changeset in the group that was belaran@999: added. All changesets between this and belaran@999: tip, inclusive, were added by a single belaran@999: hg pull, hg push or hg unbundle. belaran@999: belaran@999: belaran@999: source: A belaran@999: string. The source of these changes. See for details. belaran@999: belaran@999: belaran@999: url: A URL. The belaran@999: location of the remote repository, if known. See for more information. belaran@999: belaran@999: belaran@999: belaran@999: See also: incoming (), prechangegroup (), pretxnchangegroup () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">commit</literal>—after a new belaran@999: changeset is created belaran@999: belaran@999: This hook is run after a new changeset has been created. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: changeset ID of the newly committed changeset. belaran@999: belaran@999: belaran@999: parent1: A changeset ID. belaran@999: The changeset ID of the first parent of the newly belaran@999: committed changeset. belaran@999: belaran@999: belaran@999: parent2: A changeset ID. belaran@999: The changeset ID of the second parent of the newly belaran@999: committed changeset. belaran@999: belaran@999: belaran@999: belaran@999: See also: precommit (), pretxncommit () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">incoming</literal>—after one belaran@999: remote changeset is added belaran@999: belaran@999: This hook is run after a pre-existing changeset has been belaran@999: added to the repository, for example via a hg push. If a group of changesets belaran@999: was added in a single operation, this hook is called once for belaran@999: each added changeset. belaran@999: belaran@999: belaran@999: You can use this hook for the same purposes as belaran@999: the changegroup hook (); it's simply more belaran@999: convenient sometimes to run a hook once per group of belaran@999: changesets, while other times it's handier once per changeset. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: ID of the newly added changeset. belaran@999: belaran@999: belaran@999: source: A belaran@999: string. The source of these changes. See for details. belaran@999: belaran@999: belaran@999: url: A URL. The belaran@999: location of the remote repository, if known. See for more information. belaran@999: belaran@999: belaran@999: belaran@999: See also: changegroup () prechangegroup (), pretxnchangegroup () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">outgoing</literal>—after belaran@999: changesets are propagated belaran@999: belaran@999: This hook is run after a group of changesets has been belaran@999: propagated out of this repository, for example by a hg push or hg belaran@999: bundle command. belaran@999: belaran@999: belaran@999: One possible use for this hook is to notify administrators belaran@999: that changes have been pulled. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: changeset ID of the first changeset of the group that was belaran@999: sent. belaran@999: belaran@999: belaran@999: source: A string. The belaran@999: source of the of the operation (see ). If a remote belaran@999: client pulled changes from this repository, belaran@999: source will be belaran@999: serve. If the client that obtained belaran@999: changes from this repository was local, belaran@999: source will be belaran@999: bundle, pull, or belaran@999: push, depending on the operation the belaran@999: client performed. belaran@999: belaran@999: belaran@999: url: A URL. The belaran@999: location of the remote repository, if known. See for more information. belaran@999: belaran@999: belaran@999: belaran@999: See also: preoutgoing () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">prechangegroup</literal>—before starting belaran@999: to add remote changesets belaran@999: belaran@999: This controlling hook is run before Mercurial begins to belaran@999: add a group of changesets from another repository. belaran@999: belaran@999: belaran@999: This hook does not have any information about the belaran@999: changesets to be added, because it is run before transmission belaran@999: of those changesets is allowed to begin. If this hook fails, belaran@999: the changesets will not be transmitted. belaran@999: belaran@999: belaran@999: One use for this hook is to prevent external changes from belaran@999: being added to a repository. For example, you could use this belaran@999: to freeze a server-hosted branch temporarily or belaran@999: permanently so that users cannot push to it, while still belaran@999: allowing a local administrator to modify the repository. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: source: A string. The belaran@999: source of these changes. See for details. belaran@999: belaran@999: belaran@999: url: A URL. The belaran@999: location of the remote repository, if known. See for more information. belaran@999: belaran@999: belaran@999: belaran@999: See also: changegroup (), incoming (), pretxnchangegroup () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">precommit</literal>—before belaran@999: starting to commit a changeset belaran@999: belaran@999: This hook is run before Mercurial begins to commit a new belaran@999: changeset. It is run before Mercurial has any of the metadata belaran@999: for the commit, such as the files to be committed, the commit belaran@999: message, or the commit date. belaran@999: belaran@999: belaran@999: One use for this hook is to disable the ability to commit belaran@999: new changesets, while still allowing incoming changesets. belaran@999: Another is to run a build or test, and only allow the commit belaran@999: to begin if the build or test succeeds. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: parent1: A changeset ID. belaran@999: The changeset ID of the first parent of the working belaran@999: directory. belaran@999: belaran@999: belaran@999: parent2: A changeset ID. belaran@999: The changeset ID of the second parent of the working belaran@999: directory. belaran@999: belaran@999: belaran@999: If the commit proceeds, the parents of the working belaran@999: directory will become the parents of the new changeset. belaran@999: belaran@999: belaran@999: See also: commit belaran@999: (), pretxncommit () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">preoutgoing</literal>—before belaran@999: starting to propagate changesets belaran@999: belaran@999: This hook is invoked before Mercurial knows the identities belaran@999: of the changesets to be transmitted. belaran@999: belaran@999: belaran@999: One use for this hook is to prevent changes from being belaran@999: transmitted to another repository. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: source: A belaran@999: string. The source of the operation that is attempting to belaran@999: obtain changes from this repository (see ). See the documentation belaran@999: for the source parameter to the belaran@999: outgoing hook, in belaran@999: , for possible values belaran@999: of this parameter. belaran@999: belaran@999: belaran@999: url: A URL. The belaran@999: location of the remote repository, if known. See for more information. belaran@999: belaran@999: belaran@999: belaran@999: See also: outgoing () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">pretag</literal>—before belaran@999: tagging a changeset belaran@999: belaran@999: This controlling hook is run before a tag is created. If belaran@999: the hook succeeds, creation of the tag proceeds. If the hook belaran@999: fails, the tag is not created. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: local: A boolean. Whether belaran@999: the tag is local to this repository instance (i.e. stored belaran@999: in .hg/localtags) or belaran@999: managed by Mercurial (stored in .hgtags). belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: ID of the changeset to be tagged. belaran@999: belaran@999: belaran@999: tag: A string. The name of belaran@999: the tag to be created. belaran@999: belaran@999: belaran@999: belaran@999: If the tag to be created is belaran@999: revision-controlled, the precommit and pretxncommit hooks ( and ) will also be run. belaran@999: belaran@999: belaran@999: See also: tag belaran@999: () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">pretxnchangegroup</literal>—before belaran@999: completing addition of remote changesets belaran@999: belaran@999: This controlling hook is run before a belaran@999: transaction—that manages the addition of a group of new belaran@999: changesets from outside the repository—completes. If belaran@999: the hook succeeds, the transaction completes, and all of the belaran@999: changesets become permanent within this repository. If the belaran@999: hook fails, the transaction is rolled back, and the data for belaran@999: the changesets is erased. belaran@999: belaran@999: belaran@999: This hook can access the metadata associated with the belaran@999: almost-added changesets, but it should not do anything belaran@999: permanent with this data. It must also not modify the working belaran@999: directory. belaran@999: belaran@999: belaran@999: While this hook is running, if other Mercurial processes belaran@999: access this repository, they will be able to see the belaran@999: almost-added changesets as if they are permanent. This may belaran@999: lead to race conditions if you do not take steps to avoid belaran@999: them. belaran@999: belaran@999: belaran@999: This hook can be used to automatically vet a group of belaran@999: changesets. If the hook fails, all of the changesets are belaran@999: rejected when the transaction rolls back. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: changeset ID of the first changeset in the group that was belaran@999: added. All changesets between this and belaran@999: tip, belaran@999: inclusive, were added by a single hg pull, hg push or hg unbundle. belaran@999: belaran@999: belaran@999: source: A belaran@999: string. The source of these changes. See for details. belaran@999: belaran@999: belaran@999: url: A URL. The belaran@999: location of the remote repository, if known. See for more information. belaran@999: belaran@999: belaran@999: belaran@999: See also: changegroup (), incoming (), prechangegroup () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">pretxncommit</literal>—before belaran@999: completing commit of new changeset belaran@999: belaran@999: This controlling hook is run before a belaran@999: transaction—that manages a new commit—completes. belaran@999: If the hook succeeds, the transaction completes and the belaran@999: changeset becomes permanent within this repository. If the belaran@999: hook fails, the transaction is rolled back, and the commit belaran@999: data is erased. belaran@999: belaran@999: belaran@999: This hook can access the metadata associated with the belaran@999: almost-new changeset, but it should not do anything permanent belaran@999: with this data. It must also not modify the working belaran@999: directory. belaran@999: belaran@999: belaran@999: While this hook is running, if other Mercurial processes belaran@999: access this repository, they will be able to see the belaran@999: almost-new changeset as if it is permanent. This may lead to belaran@999: race conditions if you do not take steps to avoid them. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: changeset ID of the newly committed changeset. belaran@999: belaran@999: belaran@999: parent1: A changeset ID. belaran@999: The changeset ID of the first parent of the newly belaran@999: committed changeset. belaran@999: belaran@999: belaran@999: parent2: A changeset ID. belaran@999: The changeset ID of the second parent of the newly belaran@999: committed changeset. belaran@999: belaran@999: belaran@999: belaran@999: See also: precommit () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">preupdate</literal>—before belaran@999: updating or merging working directory belaran@999: belaran@999: This controlling hook is run before an update belaran@999: or merge of the working directory begins. It is run only if belaran@999: Mercurial's normal pre-update checks determine that the update belaran@999: or merge can proceed. If the hook succeeds, the update or belaran@999: merge may proceed; if it fails, the update or merge does not belaran@999: start. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: parent1: A belaran@999: changeset ID. The ID of the parent that the working belaran@999: directory is to be updated to. If the working directory belaran@999: is being merged, it will not change this parent. belaran@999: belaran@999: belaran@999: parent2: A belaran@999: changeset ID. Only set if the working directory is being belaran@999: merged. The ID of the revision that the working directory belaran@999: is being merged with. belaran@999: belaran@999: belaran@999: belaran@999: See also: update belaran@999: () belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">tag</literal>—after tagging a belaran@999: changeset belaran@999: belaran@999: This hook is run after a tag has been created. belaran@999: belaran@999: belaran@999: Parameters to this hook: belaran@999: belaran@999: belaran@999: local: A boolean. Whether belaran@999: the new tag is local to this repository instance (i.e. belaran@999: stored in .hg/localtags) or managed by belaran@999: Mercurial (stored in .hgtags). belaran@999: belaran@999: belaran@999: node: A changeset ID. The belaran@999: ID of the changeset that was tagged. belaran@999: belaran@999: belaran@999: tag: A string. The name of belaran@999: the tag that was created. belaran@999: belaran@999: belaran@999: belaran@999: If the created tag is revision-controlled, the commit hook (section ) is run before this hook. belaran@999: belaran@999: belaran@999: See also: pretag belaran@999: () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <literal role="hook" moreinfo="none">update</literal>—after belaran@999: updating or merging working directory belaran@999: belaran@999: This hook is run after an update or merge of the working belaran@999: directory completes. Since a merge can fail (if the external belaran@999: hgmerge command fails to resolve conflicts belaran@999: in a file), this hook communicates whether the update or merge belaran@999: completed cleanly. belaran@999: belaran@999: belaran@999: belaran@999: error: A boolean. belaran@999: Indicates whether the update or merge completed belaran@999: successfully. belaran@999: belaran@999: belaran@999: parent1: A changeset ID. belaran@999: The ID of the parent that the working directory was belaran@999: updated to. If the working directory was merged, it will belaran@999: not have changed this parent. belaran@999: belaran@999: belaran@999: parent2: A changeset ID. belaran@999: Only set if the working directory was merged. The ID of belaran@999: the revision that the working directory was merged with. belaran@999: belaran@999: belaran@999: belaran@999: See also: preupdate belaran@999: () belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Customizing the output of Mercurial belaran@999: belaran@999: Mercurial provides a powerful mechanism to let you control how belaran@999: it displays information. The mechanism is based on templates. belaran@999: You can use templates to generate specific output for a single belaran@999: command, or to customize the entire appearance of the built-in web belaran@999: interface. belaran@999: belaran@999: belaran@999: Using precanned output styles belaran@999: belaran@999: Packaged with Mercurial are some output styles that you can belaran@999: use immediately. A style is simply a precanned template that belaran@999: someone wrote and installed somewhere that Mercurial can belaran@999: find. belaran@999: belaran@999: Before we take a look at Mercurial's bundled styles, let's belaran@999: review its normal output. belaran@999: belaran@999: belaran@999: $ hg log -r1 belaran@999: changeset: 1:e3d2468ca47c belaran@999: tag: mytag belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:17 2009 +0000 belaran@999: summary: added line to end of <<hello>> file. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This is somewhat informative, but it takes up a lot of belaran@999: space—five lines of output per changeset. The belaran@999: compact style reduces this to three lines, belaran@999: presented in a sparse manner. belaran@999: belaran@999: belaran@999: $ hg log --style compact belaran@999: 3[tip] d3cc7424d32c 2009-08-16 14:05 +0000 bos belaran@999: Added tag v0.1 for changeset a5dd5392119b belaran@999: belaran@999: 2[v0.1] a5dd5392119b 2009-08-16 14:05 +0000 bos belaran@999: Added tag mytag for changeset e3d2468ca47c belaran@999: belaran@999: 1[mytag] e3d2468ca47c 2009-08-16 14:05 +0000 bos belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: 0 1cf727e9fc61 2009-08-16 14:05 +0000 bos belaran@999: added hello belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The changelog style hints at the belaran@999: expressive power of Mercurial's templating engine. This style belaran@999: attempts to follow the GNU Project's changelog belaran@999: guidelinesweb:changelog. belaran@999: belaran@999: belaran@999: $ hg log --style changelog belaran@999: 2009-08-16 Bryan O'Sullivan <bos@serpentine.com> belaran@999: belaran@999: * .hgtags: belaran@999: Added tag v0.1 for changeset a5dd5392119b belaran@999: [d3cc7424d32c] [tip] belaran@999: belaran@999: * .hgtags: belaran@999: Added tag mytag for changeset e3d2468ca47c belaran@999: [a5dd5392119b] [v0.1] belaran@999: belaran@999: * goodbye, hello: belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope belaran@999: that some might consider it so) of goodbye. belaran@999: [e3d2468ca47c] [mytag] belaran@999: belaran@999: * hello: belaran@999: added hello belaran@999: [1cf727e9fc61] belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You will not be shocked to learn that Mercurial's default belaran@999: output style is named default. belaran@999: belaran@999: belaran@999: Setting a default style belaran@999: belaran@999: You can modify the output style that Mercurial will use belaran@999: for every command by editing your ~/.hgrc file, naming the style belaran@999: you would prefer to use. belaran@999: belaran@999: [ui] belaran@999: style = compact belaran@999: belaran@999: If you write a style of your own, you can use it by either belaran@999: providing the path to your style file, or copying your style belaran@999: file into a location where Mercurial can find it (typically belaran@999: the templates subdirectory of your belaran@999: Mercurial install directory). belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Commands that support styles and templates belaran@999: belaran@999: All of Mercurial's belaran@999: log-like commands let you use belaran@999: styles and templates: hg belaran@999: incoming, hg log, belaran@999: hg outgoing, and hg tip. belaran@999: belaran@999: As I write this manual, these are so far the only commands belaran@999: that support styles and templates. Since these are the most belaran@999: important commands that need customizable output, there has been belaran@999: little pressure from the Mercurial user community to add style belaran@999: and template support to other commands. belaran@999: belaran@999: belaran@999: belaran@999: The basics of templating belaran@999: belaran@999: At its simplest, a Mercurial template is a piece of text. belaran@999: Some of the text never changes, while other parts are belaran@999: expanded, or replaced with new text, when belaran@999: necessary. belaran@999: belaran@999: Before we continue, let's look again at a simple example of belaran@999: Mercurial's normal output. belaran@999: belaran@999: belaran@999: $ hg log -r1 belaran@999: changeset: 1:e3d2468ca47c belaran@999: tag: mytag belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:17 2009 +0000 belaran@999: summary: added line to end of <<hello>> file. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Now, let's run the same command, but using a template to belaran@999: change its output. belaran@999: belaran@999: belaran@999: $ hg log -r1 --template 'i saw a changeset\n' belaran@999: i saw a changeset belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The example above illustrates the simplest possible belaran@999: template; it's just a piece of static text, printed once for belaran@999: each changeset. The option to the hg log command tells Mercurial to use belaran@999: the given text as the template when printing each belaran@999: changeset. belaran@999: belaran@999: Notice that the template string above ends with the text belaran@999: \n. This is an belaran@999: escape sequence, telling Mercurial to print belaran@999: a newline at the end of each template item. If you omit this belaran@999: newline, Mercurial will run each piece of output together. See belaran@999: for more details belaran@999: of escape sequences. belaran@999: belaran@999: A template that prints a fixed string of text all the time belaran@999: isn't very useful; let's try something a bit more belaran@999: complex. belaran@999: belaran@999: belaran@999: $ hg log --template 'i saw a changeset: {desc}\n' belaran@999: i saw a changeset: Added tag v0.1 for changeset a5dd5392119b belaran@999: i saw a changeset: Added tag mytag for changeset e3d2468ca47c belaran@999: i saw a changeset: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye. belaran@999: i saw a changeset: added hello belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: As you can see, the string belaran@999: {desc} in the template has belaran@999: been replaced in the output with the description of each belaran@999: changeset. Every time Mercurial finds text enclosed in curly belaran@999: braces ({ and belaran@999: }), it will try to replace the belaran@999: braces and text with the expansion of whatever is inside. To belaran@999: print a literal curly brace, you must escape it, as described in belaran@999: . belaran@999: belaran@999: belaran@999: belaran@999: Common template keywords belaran@999: belaran@999: You can start writing simple templates immediately using the belaran@999: keywords below. belaran@999: belaran@999: belaran@999: author: String. The belaran@999: unmodified author of the changeset. belaran@999: belaran@999: branches: String. The belaran@999: name of the branch on which the changeset was committed. belaran@999: Will be empty if the branch name was belaran@999: default. belaran@999: belaran@999: date: belaran@999: Date information. The date when the changeset was belaran@999: committed. This is not human-readable; belaran@999: you must pass it through a filter that will render it belaran@999: appropriately. See for more information belaran@999: on filters. The date is expressed as a pair of numbers. The belaran@999: first number is a Unix UTC timestamp (seconds since January belaran@999: 1, 1970); the second is the offset of the committer's belaran@999: timezone from UTC, in seconds. belaran@999: belaran@999: desc: belaran@999: String. The text of the changeset description. belaran@999: belaran@999: files: List of strings. belaran@999: All files modified, added, or removed by this belaran@999: changeset. belaran@999: belaran@999: file_adds: List of belaran@999: strings. Files added by this changeset. belaran@999: belaran@999: file_dels: List of belaran@999: strings. Files removed by this changeset. belaran@999: belaran@999: node: belaran@999: String. The changeset identification hash, as a belaran@999: 40-character hexadecimal string. belaran@999: belaran@999: parents: List of belaran@999: strings. The parents of the changeset. belaran@999: belaran@999: rev: belaran@999: Integer. The repository-local changeset revision belaran@999: number. belaran@999: belaran@999: tags: belaran@999: List of strings. Any tags associated with the belaran@999: changeset. belaran@999: belaran@999: belaran@999: belaran@999: A few simple experiments will show us what to expect when we belaran@999: use these keywords; you can see the results below. belaran@999: belaran@999: belaran@999: $ hg log -r1 --template 'author: {author}\n' belaran@999: author: Bryan O'Sullivan <bos@serpentine.com> belaran@999: $ hg log -r1 --template 'desc:\n{desc}\n' belaran@999: desc: belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye. belaran@999: $ hg log -r1 --template 'files: {files}\n' belaran@999: files: goodbye hello belaran@999: $ hg log -r1 --template 'file_adds: {file_adds}\n' belaran@999: file_adds: goodbye belaran@999: $ hg log -r1 --template 'file_dels: {file_dels}\n' belaran@999: file_dels: belaran@999: $ hg log -r1 --template 'node: {node}\n' belaran@999: node: e3d2468ca47c10bdfbbb41b367a0c84509862197 belaran@999: $ hg log -r1 --template 'parents: {parents}\n' belaran@999: parents: belaran@999: $ hg log -r1 --template 'rev: {rev}\n' belaran@999: rev: 1 belaran@999: $ hg log -r1 --template 'tags: {tags}\n' belaran@999: tags: mytag belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: As we noted above, the date keyword does not produce belaran@999: human-readable output, so we must treat it specially. This belaran@999: involves using a filter, about which more belaran@999: in . belaran@999: belaran@999: belaran@999: $ hg log -r1 --template 'date: {date}\n' belaran@999: date: 1250431517.00 belaran@999: $ hg log -r1 --template 'date: {date|isodate}\n' belaran@999: date: 2009-08-16 14:05 +0000 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Escape sequences belaran@999: belaran@999: Mercurial's templating engine recognises the most commonly belaran@999: used escape sequences in strings. When it sees a backslash belaran@999: (\) character, it looks at the belaran@999: following character and substitutes the two characters with a belaran@999: single replacement, as described below. belaran@999: belaran@999: belaran@999: \: belaran@999: Backslash, \, ASCII belaran@999: 134. belaran@999: belaran@999: \n: Newline, belaran@999: ASCII 12. belaran@999: belaran@999: \r: Carriage belaran@999: return, ASCII 15. belaran@999: belaran@999: \t: Tab, ASCII belaran@999: 11. belaran@999: belaran@999: \v: Vertical belaran@999: tab, ASCII 13. belaran@999: belaran@999: \{: Open curly belaran@999: brace, {, ASCII belaran@999: 173. belaran@999: belaran@999: \}: Close curly belaran@999: brace, }, ASCII belaran@999: 175. belaran@999: belaran@999: belaran@999: As indicated above, if you want the expansion of a template belaran@999: to contain a literal \, belaran@999: {, or belaran@999: { character, you must escape belaran@999: it. belaran@999: belaran@999: belaran@999: belaran@999: Filtering keywords to change their results belaran@999: belaran@999: Some of the results of template expansion are not belaran@999: immediately easy to use. Mercurial lets you specify an optional belaran@999: chain of filters to modify the result of belaran@999: expanding a keyword. You have already seen a common filter, belaran@999: isodate, in belaran@999: action above, to make a date readable. belaran@999: belaran@999: Below is a list of the most commonly used filters that belaran@999: Mercurial supports. While some filters can be applied to any belaran@999: text, others can only be used in specific circumstances. The belaran@999: name of each filter is followed first by an indication of where belaran@999: it can be used, then a description of its effect. belaran@999: belaran@999: belaran@999: addbreaks: Any text. Add belaran@999: an XHTML <br/> tag belaran@999: before the end of every line except the last. For example, belaran@999: foo\nbar becomes belaran@999: foo<br/>\nbar. belaran@999: belaran@999: age: date keyword. Render belaran@999: the age of the date, relative to the current time. Yields a belaran@999: string like 10 belaran@999: minutes. belaran@999: belaran@999: basename: Any text, but belaran@999: most useful for the files keyword and its belaran@999: relatives. Treat the text as a path, and return the belaran@999: basename. For example, belaran@999: foo/bar/baz becomes belaran@999: baz. belaran@999: belaran@999: date: date keyword. Render a belaran@999: date in a similar format to the Unix date command, but with belaran@999: timezone included. Yields a string like Mon belaran@999: Sep 04 15:13:13 2006 -0700. belaran@999: belaran@999: domain: Any text, belaran@999: but most useful for the author keyword. Finds belaran@999: the first string that looks like an email address, and belaran@999: extract just the domain component. For example, belaran@999: Bryan O'Sullivan belaran@999: <bos@serpentine.com> becomes belaran@999: serpentine.com. belaran@999: belaran@999: email: Any text, belaran@999: but most useful for the author keyword. Extract belaran@999: the first string that looks like an email address. For belaran@999: example, Bryan O'Sullivan belaran@999: <bos@serpentine.com> becomes belaran@999: bos@serpentine.com. belaran@999: belaran@999: escape: Any text. belaran@999: Replace the special XML/XHTML characters belaran@999: &, belaran@999: < and belaran@999: > with XML belaran@999: entities. belaran@999: belaran@999: fill68: Any text. Wrap belaran@999: the text to fit in 68 columns. This is useful before you belaran@999: pass text through the tabindent filter, and belaran@999: still want it to fit in an 80-column fixed-font belaran@999: window. belaran@999: belaran@999: fill76: Any text. Wrap belaran@999: the text to fit in 76 columns. belaran@999: belaran@999: firstline: Any text. belaran@999: Yield the first line of text, without any trailing belaran@999: newlines. belaran@999: belaran@999: hgdate: date keyword. Render belaran@999: the date as a pair of readable numbers. Yields a string belaran@999: like 1157407993 belaran@999: 25200. belaran@999: belaran@999: isodate: date keyword. Render belaran@999: the date as a text string in ISO 8601 format. Yields a belaran@999: string like 2006-09-04 15:13:13 belaran@999: -0700. belaran@999: belaran@999: obfuscate: Any text, but belaran@999: most useful for the author keyword. Yield belaran@999: the input text rendered as a sequence of XML entities. This belaran@999: helps to defeat some particularly stupid screen-scraping belaran@999: email harvesting spambots. belaran@999: belaran@999: person: Any text, belaran@999: but most useful for the author keyword. Yield belaran@999: the text before an email address. For example, belaran@999: Bryan O'Sullivan belaran@999: <bos@serpentine.com> becomes belaran@999: Bryan O'Sullivan. belaran@999: belaran@999: rfc822date: belaran@999: date keyword. belaran@999: Render a date using the same format used in email headers. belaran@999: Yields a string like Mon, 04 Sep 2006 belaran@999: 15:13:13 -0700. belaran@999: belaran@999: short: Changeset belaran@999: hash. Yield the short form of a changeset hash, i.e. a belaran@999: 12-character hexadecimal string. belaran@999: belaran@999: shortdate: date keyword. Render belaran@999: the year, month, and day of the date. Yields a string like belaran@999: 2006-09-04. belaran@999: belaran@999: strip: belaran@999: Any text. Strip all leading and trailing whitespace from belaran@999: the string. belaran@999: belaran@999: tabindent: Any text. belaran@999: Yield the text, with every line except the first starting belaran@999: with a tab character. belaran@999: belaran@999: urlescape: Any text. belaran@999: Escape all characters that are considered belaran@999: special by URL parsers. For example, belaran@999: foo bar becomes belaran@999: foo%20bar. belaran@999: belaran@999: user: Any text, belaran@999: but most useful for the author keyword. Return belaran@999: the user portion of an email address. For belaran@999: example, Bryan O'Sullivan belaran@999: <bos@serpentine.com> becomes belaran@999: bos. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: $ hg log -r1 --template '{author}\n' belaran@999: Bryan O'Sullivan <bos@serpentine.com> belaran@999: $ hg log -r1 --template '{author|domain}\n' belaran@999: serpentine.com belaran@999: $ hg log -r1 --template '{author|email}\n' belaran@999: bos@serpentine.com belaran@999: $ hg log -r1 --template '{author|obfuscate}\n' | cut -c-76 belaran@999: &#66;&#114;&#121;&#97;&#110;&#32;&#79;&#39;&#83;&#117;&#108;&#108;&#105;&#11 belaran@999: $ hg log -r1 --template '{author|person}\n' belaran@999: Bryan O'Sullivan belaran@999: $ hg log -r1 --template '{author|user}\n' belaran@999: bos belaran@999: $ hg log -r1 --template 'looks almost right, but actually garbage: {date}\n' belaran@999: looks almost right, but actually garbage: 1250431517.00 belaran@999: $ hg log -r1 --template '{date|age}\n' belaran@999: 3 seconds belaran@999: $ hg log -r1 --template '{date|date}\n' belaran@999: Sun Aug 16 14:05:17 2009 +0000 belaran@999: $ hg log -r1 --template '{date|hgdate}\n' belaran@999: 1250431517 0 belaran@999: $ hg log -r1 --template '{date|isodate}\n' belaran@999: 2009-08-16 14:05 +0000 belaran@999: $ hg log -r1 --template '{date|rfc822date}\n' belaran@999: Sun, 16 Aug 2009 14:05:17 +0000 belaran@999: $ hg log -r1 --template '{date|shortdate}\n' belaran@999: 2009-08-16 belaran@999: $ hg log -r1 --template '{desc}\n' | cut -c-76 belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope that some m belaran@999: $ hg log -r1 --template '{desc|addbreaks}\n' | cut -c-76 belaran@999: added line to end of <<hello>> file.<br/> belaran@999: <br/> belaran@999: in addition, added a file with the helpful name (at least i hope that some m belaran@999: $ hg log -r1 --template '{desc|escape}\n' | cut -c-76 belaran@999: added line to end of &lt;&lt;hello&gt;&gt; file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope that some m belaran@999: $ hg log -r1 --template '{desc|fill68}\n' belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope belaran@999: that some might consider it so) of goodbye. belaran@999: $ hg log -r1 --template '{desc|fill76}\n' belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope that some belaran@999: might consider it so) of goodbye. belaran@999: $ hg log -r1 --template '{desc|firstline}\n' belaran@999: added line to end of <<hello>> file. belaran@999: $ hg log -r1 --template '{desc|strip}\n' | cut -c-76 belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope that some m belaran@999: $ hg log -r1 --template '{desc|tabindent}\n' | expand | cut -c-76 belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope tha belaran@999: $ hg log -r1 --template '{node}\n' belaran@999: e3d2468ca47c10bdfbbb41b367a0c84509862197 belaran@999: $ hg log -r1 --template '{node|short}\n' belaran@999: e3d2468ca47c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If you try to apply a filter to a piece of data that it belaran@999: cannot process, Mercurial will fail and print a Python belaran@999: exception. For example, trying to run the output of the belaran@999: desc keyword into belaran@999: the isodate belaran@999: filter is not a good idea. belaran@999: belaran@999: belaran@999: belaran@999: Combining filters belaran@999: belaran@999: It is easy to combine filters to yield output in the form belaran@999: you would like. The following chain of filters tidies up a belaran@999: description, then makes sure that it fits cleanly into 68 belaran@999: columns, then indents it by a further 8 characters (at least belaran@999: on Unix-like systems, where a tab is conventionally 8 belaran@999: characters wide). belaran@999: belaran@999: belaran@999: $ hg log -r1 --template 'description:\n\t{desc|strip|fill68|tabindent}\n' belaran@999: description: belaran@999: added line to end of <<hello>> file. belaran@999: belaran@999: in addition, added a file with the helpful name (at least i hope belaran@999: that some might consider it so) of goodbye. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Note the use of \t (a belaran@999: tab character) in the template to force the first line to be belaran@999: indented; this is necessary since tabindent indents all belaran@999: lines except the first. belaran@999: belaran@999: Keep in mind that the order of filters in a chain is belaran@999: significant. The first filter is applied to the result of the belaran@999: keyword; the second to the result of the first filter; and so belaran@999: on. For example, using fill68|tabindent belaran@999: gives very different results from belaran@999: tabindent|fill68. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: From templates to styles belaran@999: belaran@999: A command line template provides a quick and simple way to belaran@999: format some output. Templates can become verbose, though, and belaran@999: it's useful to be able to give a template a name. A style file belaran@999: is a template with a name, stored in a file. belaran@999: belaran@999: More than that, using a style file unlocks the power of belaran@999: Mercurial's templating engine in ways that are not possible belaran@999: using the command line option. belaran@999: belaran@999: belaran@999: The simplest of style files belaran@999: belaran@999: Our simple style file contains just one line: belaran@999: belaran@999: belaran@999: $ echo 'changeset = "rev: {rev}\n"' > rev belaran@999: $ hg log -l1 --style ./rev belaran@999: rev: 3 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This tells Mercurial, if you're printing a belaran@999: changeset, use the text on the right as the belaran@999: template. belaran@999: belaran@999: belaran@999: belaran@999: Style file syntax belaran@999: belaran@999: The syntax rules for a style file are simple. belaran@999: belaran@999: belaran@999: The file is processed one line at a belaran@999: time. belaran@999: belaran@999: Leading and trailing white space are belaran@999: ignored. belaran@999: belaran@999: Empty lines are skipped. belaran@999: belaran@999: If a line starts with either of the characters belaran@999: # or belaran@999: ;, the entire line is belaran@999: treated as a comment, and skipped as if empty. belaran@999: belaran@999: A line starts with a keyword. This must start belaran@999: with an alphabetic character or underscore, and can belaran@999: subsequently contain any alphanumeric character or belaran@999: underscore. (In regexp notation, a keyword must match belaran@999: [A-Za-z_][A-Za-z0-9_]*.) belaran@999: belaran@999: The next element must be an belaran@999: = character, which can belaran@999: be preceded or followed by an arbitrary amount of white belaran@999: space. belaran@999: belaran@999: If the rest of the line starts and ends with belaran@999: matching quote characters (either single or double quote), belaran@999: it is treated as a template body. belaran@999: belaran@999: If the rest of the line does belaran@999: not start with a quote character, it is belaran@999: treated as the name of a file; the contents of this file belaran@999: will be read and used as a template body. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Style files by example belaran@999: belaran@999: To illustrate how to write a style file, we will construct a belaran@999: few by example. Rather than provide a complete style file and belaran@999: walk through it, we'll mirror the usual process of developing a belaran@999: style file by starting with something very simple, and walking belaran@999: through a series of successively more complete examples. belaran@999: belaran@999: belaran@999: Identifying mistakes in style files belaran@999: belaran@999: If Mercurial encounters a problem in a style file you are belaran@999: working on, it prints a terse error message that, once you belaran@999: figure out what it means, is actually quite useful. belaran@999: belaran@999: belaran@999: $ cat broken.style belaran@999: changeset = belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Notice that broken.style attempts to belaran@999: define a changeset keyword, but forgets to belaran@999: give any content for it. When instructed to use this style belaran@999: file, Mercurial promptly complains. belaran@999: belaran@999: belaran@999: $ hg log -r1 --style broken.style belaran@999: abort: broken.style:1: parse error belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This error message looks intimidating, but it is not too belaran@999: hard to follow. belaran@999: belaran@999: belaran@999: The first component is simply Mercurial's way belaran@999: of saying I am giving up. belaran@999: ___abort___: broken.style:1: parse error belaran@999: belaran@999: Next comes the name of the style file that belaran@999: contains the error. belaran@999: abort: ___broken.style___:1: parse error belaran@999: belaran@999: Following the file name is the line number belaran@999: where the error was encountered. belaran@999: abort: broken.style:___1___: parse error belaran@999: belaran@999: Finally, a description of what went belaran@999: wrong. belaran@999: abort: broken.style:1: ___parse error___ belaran@999: belaran@999: The description of the problem is not always belaran@999: clear (as in this case), but even when it is cryptic, it belaran@999: is almost always trivial to visually inspect the offending belaran@999: line in the style file and see what is wrong. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Uniquely identifying a repository belaran@999: belaran@999: If you would like to be able to identify a Mercurial belaran@999: repository fairly uniquely using a short string belaran@999: as an identifier, you can use the first revision in the belaran@999: repository. belaran@999: belaran@999: belaran@999: $ hg log -r0 --template '{node}' belaran@999: 02b4f9d8a52a6da645e20fa7df0accc8aa33b650 belaran@999: belaran@999: belaran@999: belaran@999: This is likely to be unique, and so it is belaran@999: useful in many cases. There are a few caveats. belaran@999: belaran@999: It will not work in a completely empty belaran@999: repository, because such a repository does not have a belaran@999: revision zero. belaran@999: belaran@999: Neither will it work in the (extremely rare) belaran@999: case where a repository is a merge of two or more formerly belaran@999: independent repositories, and you still have those belaran@999: repositories around. belaran@999: belaran@999: Here are some uses to which you could put this belaran@999: identifier: belaran@999: belaran@999: As a key into a table for a database that belaran@999: manages repositories on a server. belaran@999: belaran@999: As half of a {repository belaran@999: ID, revision ID} tuple. belaran@999: Save this information away when you run an automated build belaran@999: or other activity, so that you can replay belaran@999: the build later if necessary. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Listing files on multiple lines belaran@999: belaran@999: Suppose we want to list the files changed by a changeset, belaran@999: one per line, with a little indentation before each file belaran@999: name. belaran@999: belaran@999: belaran@999: $ cat > multiline << EOF belaran@999: > changeset = "Changed in {node|short}:\n{files}" belaran@999: > file = " {file}\n" belaran@999: > EOF belaran@999: $ hg log --style multiline belaran@999: Changed in badb58085712: belaran@999: .bashrc belaran@999: .hgrc belaran@999: test.c belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mimicking Subversion's output belaran@999: belaran@999: Let's try to emulate the default output format used by belaran@999: another revision control tool, Subversion. belaran@999: belaran@999: belaran@999: $ svn log -r9653 belaran@999: ------------------------------------------------------------------------ belaran@999: r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines belaran@999: belaran@999: On reporting a route error, also include the status for the error, belaran@999: rather than indicating a status of 0 when an error has occurred. belaran@999: belaran@999: Signed-off-by: Sean Hefty <sean.hefty@intel.com> belaran@999: belaran@999: ------------------------------------------------------------------------ belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Since Subversion's output style is fairly simple, it is belaran@999: easy to copy-and-paste a hunk of its output into a file, and belaran@999: replace the text produced above by Subversion with the belaran@999: template values we'd like to see expanded. belaran@999: belaran@999: belaran@999: $ cat svn.template belaran@999: r{rev} | {author|user} | {date|isodate} ({date|rfc822date}) belaran@999: belaran@999: {desc|strip|fill76} belaran@999: belaran@999: ------------------------------------------------------------------------ belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: There are a few small ways in which this template deviates belaran@999: from the output produced by Subversion. belaran@999: belaran@999: Subversion prints a readable belaran@999: date (the Wed, 27 Sep 2006 in the belaran@999: example output above) in parentheses. Mercurial's belaran@999: templating engine does not provide a way to display a date belaran@999: in this format without also printing the time and time belaran@999: zone. belaran@999: belaran@999: We emulate Subversion's printing of belaran@999: separator lines full of belaran@999: - characters by ending belaran@999: the template with such a line. We use the templating belaran@999: engine's header belaran@999: keyword to print a separator line as the first line of belaran@999: output (see below), thus achieving similar output to belaran@999: Subversion. belaran@999: belaran@999: Subversion's output includes a count in the belaran@999: header of the number of lines in the commit message. We belaran@999: cannot replicate this in Mercurial; the templating engine belaran@999: does not currently provide a filter that counts the number belaran@999: of lines the template generates. belaran@999: belaran@999: It took me no more than a minute or two of work to replace belaran@999: literal text from an example of Subversion's output with some belaran@999: keywords and filters to give the template above. The style belaran@999: file simply refers to the template. belaran@999: belaran@999: belaran@999: $ cat svn.style belaran@999: header = '------------------------------------------------------------------------\n\n' belaran@999: changeset = svn.template belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: We could have included the text of the template file belaran@999: directly in the style file by enclosing it in quotes and belaran@999: replacing the newlines with belaran@999: \n sequences, but it would belaran@999: have made the style file too difficult to read. Readability belaran@999: is a good guide when you're trying to decide whether some text belaran@999: belongs in a style file, or in a template file that the style belaran@999: file points to. If the style file will look too big or belaran@999: cluttered if you insert a literal piece of text, drop it into belaran@999: a template instead. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Managing change with Mercurial Queues belaran@999: belaran@999: belaran@999: The patch management problem belaran@999: belaran@999: Here is a common scenario: you need to install a software belaran@999: package from source, but you find a bug that you must fix in the belaran@999: source before you can start using the package. You make your belaran@999: changes, forget about the package for a while, and a few months belaran@999: later you need to upgrade to a newer version of the package. If belaran@999: the newer version of the package still has the bug, you must belaran@999: extract your fix from the older source tree and apply it against belaran@999: the newer version. This is a tedious task, and it's easy to belaran@999: make mistakes. belaran@999: belaran@999: This is a simple case of the patch management belaran@999: problem. You have an upstream source tree that belaran@999: you can't change; you need to make some local changes on top of belaran@999: the upstream tree; and you'd like to be able to keep those belaran@999: changes separate, so that you can apply them to newer versions belaran@999: of the upstream source. belaran@999: belaran@999: The patch management problem arises in many situations. belaran@999: Probably the most visible is that a user of an open source belaran@999: software project will contribute a bug fix or new feature to the belaran@999: project's maintainers in the form of a patch. belaran@999: belaran@999: Distributors of operating systems that include open source belaran@999: software often need to make changes to the packages they belaran@999: distribute so that they will build properly in their belaran@999: environments. belaran@999: belaran@999: When you have few changes to maintain, it is easy to manage belaran@999: a single patch using the standard diff and belaran@999: patch programs (see for a discussion of these belaran@999: tools). Once the number of changes grows, it starts to make belaran@999: sense to maintain patches as discrete chunks of belaran@999: work, so that for example a single patch will contain belaran@999: only one bug fix (the patch might modify several files, but it's belaran@999: doing only one thing), and you may have a number belaran@999: of such patches for different bugs you need fixed and local belaran@999: changes you require. In this situation, if you submit a bug fix belaran@999: patch to the upstream maintainers of a package and they include belaran@999: your fix in a subsequent release, you can simply drop that belaran@999: single patch when you're updating to the newer release. belaran@999: belaran@999: Maintaining a single patch against an upstream tree is a belaran@999: little tedious and error-prone, but not difficult. However, the belaran@999: complexity of the problem grows rapidly as the number of patches belaran@999: you have to maintain increases. With more than a tiny number of belaran@999: patches in hand, understanding which ones you have applied and belaran@999: maintaining them moves from messy to overwhelming. belaran@999: belaran@999: Fortunately, Mercurial includes a powerful extension, belaran@999: Mercurial Queues (or simply MQ), that massively belaran@999: simplifies the patch management problem. belaran@999: belaran@999: belaran@999: belaran@999: The prehistory of Mercurial Queues belaran@999: belaran@999: During the late 1990s, several Linux kernel developers belaran@999: started to maintain patch series that modified belaran@999: the behavior of the Linux kernel. Some of these series were belaran@999: focused on stability, some on feature coverage, and others were belaran@999: more speculative. belaran@999: belaran@999: The sizes of these patch series grew rapidly. In 2002, belaran@999: Andrew Morton published some shell scripts he had been using to belaran@999: automate the task of managing his patch queues. Andrew was belaran@999: successfully using these scripts to manage hundreds (sometimes belaran@999: thousands) of patches on top of the Linux kernel. belaran@999: belaran@999: belaran@999: A patchwork quilt belaran@999: belaran@999: In early 2003, Andreas Gruenbacher and Martin Quinson belaran@999: borrowed the approach of Andrew's scripts and published a tool belaran@999: called patchwork quilt belaran@999: web:quilt, or simply quilt belaran@999: (see gruenbacher:2005 for a paper belaran@999: describing it). Because quilt substantially automated patch belaran@999: management, it rapidly gained a large following among open belaran@999: source software developers. belaran@999: belaran@999: Quilt manages a stack of patches on belaran@999: top of a directory tree. To begin, you tell quilt to manage a belaran@999: directory tree, and tell it which files you want to manage; it belaran@999: stores away the names and contents of those files. To fix a belaran@999: bug, you create a new patch (using a single command), edit the belaran@999: files you need to fix, then refresh the belaran@999: patch. belaran@999: belaran@999: The refresh step causes quilt to scan the directory tree; belaran@999: it updates the patch with all of the changes you have made. belaran@999: You can create another patch on top of the first, which will belaran@999: track the changes required to modify the tree from tree belaran@999: with one patch applied to tree with two belaran@999: patches applied. belaran@999: belaran@999: You can change which patches are belaran@999: applied to the tree. If you pop a patch, the belaran@999: changes made by that patch will vanish from the directory belaran@999: tree. Quilt remembers which patches you have popped, though, belaran@999: so you can push a popped patch again, and the belaran@999: directory tree will be restored to contain the modifications belaran@999: in the patch. Most importantly, you can run the belaran@999: refresh command at any time, and the topmost belaran@999: applied patch will be updated. This means that you can, at belaran@999: any time, change both which patches are applied and what belaran@999: modifications those patches make. belaran@999: belaran@999: Quilt knows nothing about revision control tools, so it belaran@999: works equally well on top of an unpacked tarball or a belaran@999: Subversion working copy. belaran@999: belaran@999: belaran@999: belaran@999: From patchwork quilt to Mercurial Queues belaran@999: belaran@999: In mid-2005, Chris Mason took the features of quilt and belaran@999: wrote an extension that he called Mercurial Queues, which belaran@999: added quilt-like behavior to Mercurial. belaran@999: belaran@999: The key difference between quilt and MQ is that quilt belaran@999: knows nothing about revision control systems, while MQ is belaran@999: integrated into Mercurial. Each patch belaran@999: that you push is represented as a Mercurial changeset. Pop a belaran@999: patch, and the changeset goes away. belaran@999: belaran@999: Because quilt does not care about revision control tools, belaran@999: it is still a tremendously useful piece of software to know belaran@999: about for situations where you cannot use Mercurial and belaran@999: MQ. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The huge advantage of MQ belaran@999: belaran@999: I cannot overstate the value that MQ offers through the belaran@999: unification of patches and revision control. belaran@999: belaran@999: A major reason that patches have persisted in the free belaran@999: software and open source world—in spite of the belaran@999: availability of increasingly capable revision control tools over belaran@999: the years—is the agility they belaran@999: offer. belaran@999: belaran@999: Traditional revision control tools make a permanent, belaran@999: irreversible record of everything that you do. While this has belaran@999: great value, it's also somewhat stifling. If you want to belaran@999: perform a wild-eyed experiment, you have to be careful in how belaran@999: you go about it, or you risk leaving unneeded—or worse, belaran@999: misleading or destabilising—traces of your missteps and belaran@999: errors in the permanent revision record. belaran@999: belaran@999: By contrast, MQ's marriage of distributed revision control belaran@999: with patches makes it much easier to isolate your work. Your belaran@999: patches live on top of normal revision history, and you can make belaran@999: them disappear or reappear at will. If you don't like a patch, belaran@999: you can drop it. If a patch isn't quite as you want it to be, belaran@999: simply fix it—as many times as you need to, until you belaran@999: have refined it into the form you desire. belaran@999: belaran@999: As an example, the integration of patches with revision belaran@999: control makes understanding patches and debugging their belaran@999: effects—and their interplay with the code they're based belaran@999: on—enormously easier. Since every belaran@999: applied patch has an associated changeset, you can give hg log a file name to see which belaran@999: changesets and patches affected the file. You can use the belaran@999: hg bisect command to belaran@999: binary-search through all changesets and applied patches to see belaran@999: where a bug got introduced or fixed. You can use the hg annotate command to see which belaran@999: changeset or patch modified a particular line of a source file. belaran@999: And so on. belaran@999: belaran@999: belaran@999: belaran@999: Understanding patches belaran@999: belaran@999: Because MQ doesn't hide its patch-oriented nature, it is belaran@999: helpful to understand what patches are, and a little about the belaran@999: tools that work with them. belaran@999: belaran@999: The traditional Unix diff command belaran@999: compares two files, and prints a list of differences between belaran@999: them. The patch command understands these belaran@999: differences as modifications to make to a belaran@999: file. Take a look below for a simple example of these commands belaran@999: in action. belaran@999: belaran@999: belaran@999: $ echo 'this is my original thought' > oldfile belaran@999: $ echo 'i have changed my mind' > newfile belaran@999: $ diff -u oldfile newfile > tiny.patch belaran@999: $ cat tiny.patch belaran@999: --- oldfile 2009-08-16 14:05:06.000000000 +0000 belaran@999: +++ newfile 2009-08-16 14:05:06.000000000 +0000 belaran@999: @@ -1 +1 @@ belaran@999: -this is my original thought belaran@999: +i have changed my mind belaran@999: $ patch < tiny.patch belaran@999: patching file oldfile belaran@999: $ cat oldfile belaran@999: i have changed my mind belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The type of file that diff generates (and belaran@999: patch takes as input) is called a belaran@999: patch or a diff; there is no belaran@999: difference between a patch and a diff. (We'll use the term belaran@999: patch, since it's more commonly used.) belaran@999: belaran@999: A patch file can start with arbitrary text; the belaran@999: patch command ignores this text, but MQ uses belaran@999: it as the commit message when creating changesets. To find the belaran@999: beginning of the patch content, patch belaran@999: searches for the first line that starts with the string belaran@999: diff -. belaran@999: belaran@999: MQ works with unified diffs belaran@999: (patch can accept several other diff formats, belaran@999: but MQ doesn't). A unified diff contains two kinds of header. belaran@999: The file header describes the file being belaran@999: modified; it contains the name of the file to modify. When belaran@999: patch sees a new file header, it looks for a belaran@999: file with that name to start modifying. belaran@999: belaran@999: After the file header comes a series of belaran@999: hunks. Each hunk starts with a header; belaran@999: this identifies the range of line numbers within the file that belaran@999: the hunk should modify. Following the header, a hunk starts and belaran@999: ends with a few (usually three) lines of text from the belaran@999: unmodified file; these are called the belaran@999: context for the hunk. If there's only a belaran@999: small amount of context between successive hunks, belaran@999: diff doesn't print a new hunk header; it just belaran@999: runs the hunks together, with a few lines of context between belaran@999: modifications. belaran@999: belaran@999: Each line of context begins with a space character. Within belaran@999: the hunk, a line that begins with belaran@999: - means remove this belaran@999: line, while a line that begins with belaran@999: + means insert this belaran@999: line. For example, a line that is modified is belaran@999: represented by one deletion and one insertion. belaran@999: belaran@999: We will return to some of the more subtle aspects of patches belaran@999: later (in ), but you belaran@999: should have belaran@999: enough information now to use MQ. belaran@999: belaran@999: belaran@999: belaran@999: Getting started with Mercurial Queues belaran@999: belaran@999: Because MQ is implemented as an extension, you must belaran@999: explicitly enable before you can use it. (You don't need to belaran@999: download anything; MQ ships with the standard Mercurial belaran@999: distribution.) To enable MQ, edit your ~/.hgrc file, and add the lines belaran@999: below. belaran@999: belaran@999: [extensions] belaran@999: hgext.mq = belaran@999: belaran@999: Once the extension is enabled, it will make a number of new belaran@999: commands available. To verify that the extension is working, belaran@999: you can use hg help to see if belaran@999: the qinit command is now belaran@999: available. belaran@999: belaran@999: belaran@999: $ hg help qinit belaran@999: hg qinit [-c] belaran@999: belaran@999: init a new queue repository belaran@999: belaran@999: The queue repository is unversioned by default. If -c is belaran@999: specified, qinit will create a separate nested repository belaran@999: for patches (qinit -c may also be run later to convert belaran@999: an unversioned patch repository into a versioned one). belaran@999: You can use qcommit to commit changes to this queue repository. belaran@999: belaran@999: options: belaran@999: belaran@999: -c --create-repo create queue repository belaran@999: belaran@999: use "hg -v help qinit" to show global options belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You can use MQ with any Mercurial belaran@999: repository, and its commands only operate within that belaran@999: repository. To get started, simply prepare the repository using belaran@999: the qinit command. belaran@999: belaran@999: belaran@999: $ hg init mq-sandbox belaran@999: $ cd mq-sandbox belaran@999: $ echo 'line 1' > file1 belaran@999: $ echo 'another line 1' > file2 belaran@999: $ hg add file1 file2 belaran@999: $ hg commit -m'first change' belaran@999: $ hg qinit belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This command creates an empty directory called .hg/patches, where belaran@999: MQ will keep its metadata. As with many Mercurial commands, the belaran@999: qinit command prints nothing belaran@999: if it succeeds. belaran@999: belaran@999: belaran@999: Creating a new patch belaran@999: belaran@999: To begin work on a new patch, use the qnew command. This command takes belaran@999: one argument, the name of the patch to create. belaran@999: belaran@999: MQ will use this as the name of an actual file in the belaran@999: .hg/patches directory, as you belaran@999: can see below. belaran@999: belaran@999: belaran@999: $ hg tip belaran@999: changeset: 0:5d84c303994b belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:11 2009 +0000 belaran@999: summary: first change belaran@999: belaran@999: $ hg qnew first.patch belaran@999: $ hg tip belaran@999: changeset: 1:ba4d7a3f2149 belaran@999: tag: qtip belaran@999: tag: first.patch belaran@999: tag: tip belaran@999: tag: qbase belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:11 2009 +0000 belaran@999: summary: [mq]: first.patch belaran@999: belaran@999: $ ls .hg/patches belaran@999: first.patch series status belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Also newly present in the .hg/patches directory are two belaran@999: other files, series and belaran@999: status. The series file lists all of the belaran@999: patches that MQ knows about for this repository, with one belaran@999: patch per line. Mercurial uses the status file for internal belaran@999: book-keeping; it tracks all of the patches that MQ has belaran@999: applied in this repository. belaran@999: belaran@999: belaran@999: You may sometimes want to edit the series file by hand; for belaran@999: example, to change the sequence in which some patches are belaran@999: applied. However, manually editing the status file is almost always a belaran@999: bad idea, as it's easy to corrupt MQ's idea of what is belaran@999: happening. belaran@999: belaran@999: belaran@999: Once you have created your new patch, you can edit files belaran@999: in the working directory as you usually would. All of the belaran@999: normal Mercurial commands, such as hg belaran@999: diff and hg belaran@999: annotate, work exactly as they did before. belaran@999: belaran@999: belaran@999: belaran@999: Refreshing a patch belaran@999: belaran@999: When you reach a point where you want to save your work, belaran@999: use the qrefresh command belaran@999: to update the patch you are working on. belaran@999: belaran@999: belaran@999: $ echo 'line 2' >> file1 belaran@999: $ hg diff belaran@999: diff -r ba4d7a3f2149 file1 belaran@999: --- a/file1 Sun Aug 16 14:05:11 2009 +0000 belaran@999: +++ b/file1 Sun Aug 16 14:05:11 2009 +0000 belaran@999: @@ -1,1 +1,2 @@ belaran@999: line 1 belaran@999: +line 2 belaran@999: $ hg qrefresh belaran@999: $ hg diff belaran@999: $ hg tip --style=compact --patch belaran@999: 1[qtip,first.patch,tip,qbase] 1aa236e17e55 2009-08-16 14:05 +0000 bos belaran@999: [mq]: first.patch belaran@999: belaran@999: diff -r 5d84c303994b -r 1aa236e17e55 file1 belaran@999: --- a/file1 Sun Aug 16 14:05:11 2009 +0000 belaran@999: +++ b/file1 Sun Aug 16 14:05:11 2009 +0000 belaran@999: @@ -1,1 +1,2 @@ belaran@999: line 1 belaran@999: +line 2 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This command folds the changes you have made in the belaran@999: working directory into your patch, and updates its belaran@999: corresponding changeset to contain those changes. belaran@999: belaran@999: You can run qrefresh belaran@999: as often as you like, so it's a good way to belaran@999: checkpoint your work. Refresh your patch at an belaran@999: opportune time; try an experiment; and if the experiment belaran@999: doesn't work out, hg revert belaran@999: your modifications back to the last time you refreshed. belaran@999: belaran@999: belaran@999: $ echo 'line 3' >> file1 belaran@999: $ hg status belaran@999: M file1 belaran@999: $ hg qrefresh belaran@999: $ hg tip --style=compact --patch belaran@999: 1[qtip,first.patch,tip,qbase] ebec7ce95e11 2009-08-16 14:05 +0000 bos belaran@999: [mq]: first.patch belaran@999: belaran@999: diff -r 5d84c303994b -r ebec7ce95e11 file1 belaran@999: --- a/file1 Sun Aug 16 14:05:11 2009 +0000 belaran@999: +++ b/file1 Sun Aug 16 14:05:12 2009 +0000 belaran@999: @@ -1,1 +1,3 @@ belaran@999: line 1 belaran@999: +line 2 belaran@999: +line 3 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Stacking and tracking patches belaran@999: belaran@999: Once you have finished working on a patch, or need to work belaran@999: on another, you can use the qnew command again to create a belaran@999: new patch. Mercurial will apply this patch on top of your belaran@999: existing patch. belaran@999: belaran@999: belaran@999: $ hg qnew second.patch belaran@999: $ hg log --style=compact --limit=2 belaran@999: 2[qtip,second.patch,tip] dffbc4265523 2009-08-16 14:05 +0000 bos belaran@999: [mq]: second.patch belaran@999: belaran@999: 1[first.patch,qbase] ebec7ce95e11 2009-08-16 14:05 +0000 bos belaran@999: [mq]: first.patch belaran@999: belaran@999: $ echo 'line 4' >> file1 belaran@999: $ hg qrefresh belaran@999: $ hg tip --style=compact --patch belaran@999: 2[qtip,second.patch,tip] fdacb9b232ac 2009-08-16 14:05 +0000 bos belaran@999: [mq]: second.patch belaran@999: belaran@999: diff -r ebec7ce95e11 -r fdacb9b232ac file1 belaran@999: --- a/file1 Sun Aug 16 14:05:12 2009 +0000 belaran@999: +++ b/file1 Sun Aug 16 14:05:12 2009 +0000 belaran@999: @@ -1,3 +1,4 @@ belaran@999: line 1 belaran@999: line 2 belaran@999: line 3 belaran@999: +line 4 belaran@999: belaran@999: $ hg annotate file1 belaran@999: 0: line 1 belaran@999: 1: line 2 belaran@999: 1: line 3 belaran@999: 2: line 4 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Notice that the patch contains the changes in our prior belaran@999: patch as part of its context (you can see this more clearly in belaran@999: the output of hg belaran@999: annotate). belaran@999: belaran@999: So far, with the exception of qnew and qrefresh, we've been careful to belaran@999: only use regular Mercurial commands. However, MQ provides belaran@999: many commands that are easier to use when you are thinking belaran@999: about patches, as illustrated below. belaran@999: belaran@999: belaran@999: $ hg qseries belaran@999: first.patch belaran@999: second.patch belaran@999: $ hg qapplied belaran@999: first.patch belaran@999: second.patch belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The qseries command lists every belaran@999: patch that MQ knows about in this repository, from oldest belaran@999: to newest (most recently belaran@999: created). belaran@999: belaran@999: The qapplied command lists every belaran@999: patch that MQ has applied in this belaran@999: repository, again from oldest to newest (most recently belaran@999: applied). belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Manipulating the patch stack belaran@999: belaran@999: The previous discussion implied that there must be a belaran@999: difference between known and belaran@999: applied patches, and there is. MQ can manage a belaran@999: patch without it being applied in the repository. belaran@999: belaran@999: An applied patch has a corresponding belaran@999: changeset in the repository, and the effects of the patch and belaran@999: changeset are visible in the working directory. You can undo belaran@999: the application of a patch using the qpop command. MQ still belaran@999: knows about, or manages, a popped patch, belaran@999: but the patch no longer has a corresponding changeset in the belaran@999: repository, and the working directory does not contain the belaran@999: changes made by the patch. illustrates belaran@999: the difference between applied and tracked patches. belaran@999: belaran@999:
belaran@999: Applied and unapplied patches in the MQ patch belaran@999: stack belaran@999: belaran@999: belaran@999: XXX add text belaran@999: belaran@999:
belaran@999: belaran@999: You can reapply an unapplied, or popped, patch using the belaran@999: qpush command. This belaran@999: creates a new changeset to correspond to the patch, and the belaran@999: patch's changes once again become present in the working belaran@999: directory. See below for examples of qpop and qpush in action. belaran@999: belaran@999: belaran@999: $ hg qapplied belaran@999: first.patch belaran@999: second.patch belaran@999: $ hg qpop belaran@999: now at: first.patch belaran@999: $ hg qseries belaran@999: first.patch belaran@999: second.patch belaran@999: $ hg qapplied belaran@999: first.patch belaran@999: $ cat file1 belaran@999: line 1 belaran@999: line 2 belaran@999: line 3 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Notice that once we have popped a patch or two patches, belaran@999: the output of qseries belaran@999: remains the same, while that of qapplied has changed. belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: Pushing and popping many patches belaran@999: belaran@999: While qpush and belaran@999: qpop each operate on a belaran@999: single patch at a time by default, you can push and pop many belaran@999: patches in one go. The option to belaran@999: qpush causes it to push belaran@999: all unapplied patches, while the option to qpop causes it to pop all applied belaran@999: patches. (For some more ways to push and pop many patches, belaran@999: see below.) belaran@999: belaran@999: belaran@999: $ hg qpush -a belaran@999: applying second.patch belaran@999: now at: second.patch belaran@999: $ cat file1 belaran@999: line 1 belaran@999: line 2 belaran@999: line 3 belaran@999: line 4 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Safety checks, and overriding them belaran@999: belaran@999: Several MQ commands check the working directory before belaran@999: they do anything, and fail if they find any modifications. belaran@999: They do this to ensure that you won't lose any changes that belaran@999: you have made, but not yet incorporated into a patch. The belaran@999: example below illustrates this; the qnew command will not create a belaran@999: new patch if there are outstanding changes, caused in this belaran@999: case by the hg add of belaran@999: file3. belaran@999: belaran@999: belaran@999: $ echo 'file 3, line 1' >> file3 belaran@999: $ hg qnew add-file3.patch belaran@999: $ hg qnew -f add-file3.patch belaran@999: abort: patch "add-file3.patch" already exists belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Commands that check the working directory all take an belaran@999: I know what I'm doing option, which is always belaran@999: named . The exact meaning of belaran@999: depends on the command. For example, belaran@999: hg qnew belaran@999: will incorporate any outstanding changes into the new patch it belaran@999: creates, but hg qpop belaran@999: will revert modifications to any files affected by the patch belaran@999: that it is popping. Be sure to read the documentation for a belaran@999: command's option before you use it! belaran@999: belaran@999: belaran@999: belaran@999: Working on several patches at once belaran@999: belaran@999: The qrefresh command belaran@999: always refreshes the topmost applied belaran@999: patch. This means that you can suspend work on one patch (by belaran@999: refreshing it), pop or push to make a different patch the top, belaran@999: and work on that patch for a belaran@999: while. belaran@999: belaran@999: Here's an example that illustrates how you can use this belaran@999: ability. Let's say you're developing a new feature as two belaran@999: patches. The first is a change to the core of your software, belaran@999: and the second—layered on top of the belaran@999: first—changes the user interface to use the code you belaran@999: just added to the core. If you notice a bug in the core while belaran@999: you're working on the UI patch, it's easy to fix the core. belaran@999: Simply qrefresh the UI belaran@999: patch to save your in-progress changes, and qpop down to the core patch. Fix belaran@999: the core bug, qrefresh the belaran@999: core patch, and qpush back belaran@999: to the UI patch to continue where you left off. belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: More about patches belaran@999: belaran@999: MQ uses the GNU patch command to apply belaran@999: patches, so it's helpful to know a few more detailed aspects of belaran@999: how patch works, and about patches belaran@999: themselves. belaran@999: belaran@999: belaran@999: The strip count belaran@999: belaran@999: If you look at the file headers in a patch, you will belaran@999: notice that the pathnames usually have an extra component on belaran@999: the front that isn't present in the actual path name. This is belaran@999: a holdover from the way that people used to generate patches belaran@999: (people still do this, but it's somewhat rare with modern belaran@999: revision control tools). belaran@999: belaran@999: Alice would unpack a tarball, edit her files, then decide belaran@999: that she wanted to create a patch. So she'd rename her belaran@999: working directory, unpack the tarball again (hence the need belaran@999: for the rename), and use the and options to belaran@999: diff to recursively generate a patch belaran@999: between the unmodified directory and the modified one. The belaran@999: result would be that the name of the unmodified directory belaran@999: would be at the front of the left-hand path in every file belaran@999: header, and the name of the modified directory would be at the belaran@999: front of the right-hand path. belaran@999: belaran@999: Since someone receiving a patch from the Alices of the net belaran@999: would be unlikely to have unmodified and modified directories belaran@999: with exactly the same names, the patch belaran@999: command has a option belaran@999: that indicates the number of leading path name components to belaran@999: strip when trying to apply a patch. This number is called the belaran@999: strip count. belaran@999: belaran@999: An option of -p1 means belaran@999: use a strip count of one. If belaran@999: patch sees a file name belaran@999: foo/bar/baz in a file header, it will belaran@999: strip foo and try to patch a file named belaran@999: bar/baz. (Strictly speaking, the strip belaran@999: count refers to the number of path belaran@999: separators (and the components that go with them belaran@999: ) to strip. A strip count of one will turn belaran@999: foo/bar into bar, belaran@999: but /foo/bar (notice the extra leading belaran@999: slash) into foo/bar.) belaran@999: belaran@999: The standard strip count for patches is belaran@999: one; almost all patches contain one leading path name belaran@999: component that needs to be stripped. Mercurial's hg diff command generates path names belaran@999: in this form, and the hg belaran@999: import command and MQ expect patches to have a belaran@999: strip count of one. belaran@999: belaran@999: If you receive a patch from someone that you want to add belaran@999: to your patch queue, and the patch needs a strip count other belaran@999: than one, you cannot just qimport the patch, because belaran@999: qimport does not yet have belaran@999: a -p option (see issue belaran@999: 311). Your best bet is to qnew a patch of your own, then belaran@999: use patch -pN to apply their patch, belaran@999: followed by hg addremove to belaran@999: pick up any files added or removed by the patch, followed by belaran@999: hg qrefresh. This belaran@999: complexity may become unnecessary; see issue belaran@999: 311 for details. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Strategies for applying a patch belaran@999: belaran@999: When patch applies a hunk, it tries a belaran@999: handful of successively less accurate strategies to try to belaran@999: make the hunk apply. This falling-back technique often makes belaran@999: it possible to take a patch that was generated against an old belaran@999: version of a file, and apply it against a newer version of belaran@999: that file. belaran@999: belaran@999: First, patch tries an exact match, belaran@999: where the line numbers, the context, and the text to be belaran@999: modified must apply exactly. If it cannot make an exact belaran@999: match, it tries to find an exact match for the context, belaran@999: without honouring the line numbering information. If this belaran@999: succeeds, it prints a line of output saying that the hunk was belaran@999: applied, but at some offset from the belaran@999: original line number. belaran@999: belaran@999: If a context-only match fails, patch belaran@999: removes the first and last lines of the context, and tries a belaran@999: reduced context-only match. If the hunk belaran@999: with reduced context succeeds, it prints a message saying that belaran@999: it applied the hunk with a fuzz factor belaran@999: (the number after the fuzz factor indicates how many lines of belaran@999: context patch had to trim before the patch belaran@999: applied). belaran@999: belaran@999: When neither of these techniques works, belaran@999: patch prints a message saying that the hunk belaran@999: in question was rejected. It saves rejected hunks (also belaran@999: simply called rejects) to a file with the same belaran@999: name, and an added .rej belaran@999: extension. It also saves an unmodified copy of the file with belaran@999: a .orig extension; the belaran@999: copy of the file without any extensions will contain any belaran@999: changes made by hunks that did apply belaran@999: cleanly. If you have a patch that modifies belaran@999: foo with six hunks, and one of them fails belaran@999: to apply, you will have: an unmodified belaran@999: foo.orig, a foo.rej belaran@999: containing one hunk, and foo, containing belaran@999: the changes made by the five successful hunks. belaran@999: belaran@999: belaran@999: belaran@999: Some quirks of patch representation belaran@999: belaran@999: There are a few useful things to know about how belaran@999: patch works with files. belaran@999: belaran@999: This should already be obvious, but belaran@999: patch cannot handle binary belaran@999: files. belaran@999: belaran@999: Neither does it care about the executable bit; belaran@999: it creates new files as readable, but not belaran@999: executable. belaran@999: belaran@999: patch treats the removal of belaran@999: a file as a diff between the file to be removed and the belaran@999: empty file. So your idea of I deleted this belaran@999: file looks like every line of this file belaran@999: was deleted in a patch. belaran@999: belaran@999: It treats the addition of a file as a diff belaran@999: between the empty file and the file to be added. So in a belaran@999: patch, your idea of I added this file looks belaran@999: like every line of this file was belaran@999: added. belaran@999: belaran@999: It treats a renamed file as the removal of the belaran@999: old name, and the addition of the new name. This means belaran@999: that renamed files have a big footprint in patches. (Note belaran@999: also that Mercurial does not currently try to infer when belaran@999: files have been renamed or copied in a patch.) belaran@999: belaran@999: patch cannot represent belaran@999: empty files, so you cannot use a patch to represent the belaran@999: notion I added this empty file to the belaran@999: tree. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Beware the fuzz belaran@999: belaran@999: While applying a hunk at an offset, or with a fuzz factor, belaran@999: will often be completely successful, these inexact techniques belaran@999: naturally leave open the possibility of corrupting the patched belaran@999: file. The most common cases typically involve applying a belaran@999: patch twice, or at an incorrect location in the file. If belaran@999: patch or qpush ever mentions an offset or belaran@999: fuzz factor, you should make sure that the modified files are belaran@999: correct afterwards. belaran@999: belaran@999: It's often a good idea to refresh a patch that has applied belaran@999: with an offset or fuzz factor; refreshing the patch generates belaran@999: new context information that will make it apply cleanly. I belaran@999: say often, not always, because belaran@999: sometimes refreshing a patch will make it fail to apply belaran@999: against a different revision of the underlying files. In some belaran@999: cases, such as when you're maintaining a patch that must sit belaran@999: on top of multiple versions of a source tree, it's acceptable belaran@999: to have a patch apply with some fuzz, provided you've verified belaran@999: the results of the patching process in such cases. belaran@999: belaran@999: belaran@999: belaran@999: Handling rejection belaran@999: belaran@999: If qpush fails to belaran@999: apply a patch, it will print an error message and exit. If it belaran@999: has left .rej files belaran@999: behind, it is usually best to fix up the rejected hunks before belaran@999: you push more patches or do any further work. belaran@999: belaran@999: If your patch used to apply cleanly, belaran@999: and no longer does because you've changed the underlying code belaran@999: that your patches are based on, Mercurial Queues can help; see belaran@999: for details. belaran@999: belaran@999: Unfortunately, there aren't any great techniques for belaran@999: dealing with rejected hunks. Most often, you'll need to view belaran@999: the .rej file and edit the belaran@999: target file, applying the rejected hunks by hand. belaran@999: belaran@999: A Linux kernel hacker, Chris Mason (the author belaran@999: of Mercurial Queues), wrote a tool called belaran@999: mpatch (http://oss.oracle.com/~mason/mpatch/), belaran@999: which takes a simple approach to automating the application of belaran@999: hunks rejected by patch. The belaran@999: mpatch command can help with four common belaran@999: reasons that a hunk may be rejected: belaran@999: belaran@999: belaran@999: The context in the middle of a hunk has belaran@999: changed. belaran@999: belaran@999: A hunk is missing some context at the belaran@999: beginning or end. belaran@999: belaran@999: A large hunk might apply better—either belaran@999: entirely or in part—if it was broken up into belaran@999: smaller hunks. belaran@999: belaran@999: A hunk removes lines with slightly different belaran@999: content than those currently present in the file. belaran@999: belaran@999: belaran@999: If you use mpatch, you belaran@999: should be doubly careful to check your results when you're belaran@999: done. In fact, mpatch enforces this method belaran@999: of double-checking the tool's output, by automatically belaran@999: dropping you into a merge program when it has done its job, so belaran@999: that you can verify its work and finish off any remaining belaran@999: merges. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: More on patch management belaran@999: belaran@999: As you grow familiar with MQ, you will find yourself wanting belaran@999: to perform other kinds of patch management operations. belaran@999: belaran@999: belaran@999: Deleting unwanted patches belaran@999: belaran@999: If you want to get rid of a patch, use the hg qdelete command to delete the belaran@999: patch file and remove its entry from the patch series. If you belaran@999: try to delete a patch that is still applied, hg qdelete will refuse. belaran@999: belaran@999: belaran@999: $ hg init myrepo belaran@999: $ cd myrepo belaran@999: $ hg qinit belaran@999: $ hg qnew bad.patch belaran@999: $ echo a > a belaran@999: $ hg add a belaran@999: $ hg qrefresh belaran@999: $ hg qdelete bad.patch belaran@999: abort: cannot delete applied patch bad.patch belaran@999: $ hg qpop belaran@999: patch queue now empty belaran@999: $ hg qdelete bad.patch belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Converting to and from permanent revisions belaran@999: belaran@999: Once you're done working on a patch and want to belaran@999: turn it into a permanent changeset, use the hg qfinish command. Pass a revision belaran@999: to the command to identify the patch that you want to turn into belaran@999: a regular changeset; this patch must already be applied. belaran@999: belaran@999: belaran@999: $ hg qnew good.patch belaran@999: $ echo a > a belaran@999: $ hg add a belaran@999: $ hg qrefresh -m 'Good change' belaran@999: $ hg qfinish tip belaran@999: $ hg qapplied belaran@999: $ hg tip --style=compact belaran@999: 0[tip] 32fc5ce6b092 2009-08-16 14:04 +0000 bos belaran@999: Good change belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The hg qfinish command belaran@999: accepts an or belaran@999: option, which turns all applied patches into regular belaran@999: changesets. belaran@999: belaran@999: It is also possible to turn an existing changeset into a belaran@999: patch, by passing the option to hg qimport. belaran@999: belaran@999: belaran@999: $ hg qimport -r tip belaran@999: $ hg qapplied belaran@999: 0.diff belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Note that it only makes sense to convert a changeset into belaran@999: a patch if you have not propagated that changeset into any belaran@999: other repositories. The imported changeset's ID will change belaran@999: every time you refresh the patch, which will make Mercurial belaran@999: treat it as unrelated to the original changeset if you have belaran@999: pushed it somewhere else. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Getting the best performance out of MQ belaran@999: belaran@999: MQ is very efficient at handling a large number belaran@999: of patches. I ran some performance experiments in mid-2006 for a belaran@999: talk that I gave at the 2006 EuroPython conference (on modern belaran@999: hardware, you should expect better performance than you'll see belaran@999: below). I used as my data set the Linux 2.6.17-mm1 patch belaran@999: series, which consists of 1,738 patches. I applied these on top belaran@999: of a Linux kernel repository containing all 27,472 revisions belaran@999: between Linux 2.6.12-rc2 and Linux 2.6.17. belaran@999: belaran@999: On my old, slow laptop, I was able to hg qpush all belaran@999: 1,738 patches in 3.5 minutes, and hg qpop belaran@999: belaran@999: them all in 30 seconds. (On a newer laptop, the time to push belaran@999: all patches dropped to two minutes.) I could qrefresh one of the biggest patches belaran@999: (which made 22,779 lines of changes to 287 files) in 6.6 belaran@999: seconds. belaran@999: belaran@999: Clearly, MQ is well suited to working in large trees, but belaran@999: there are a few tricks you can use to get the best performance belaran@999: of it. belaran@999: belaran@999: First of all, try to batch operations belaran@999: together. Every time you run qpush or qpop, these commands scan the belaran@999: working directory once to make sure you haven't made some belaran@999: changes and then forgotten to run qrefresh. On a small tree, the belaran@999: time that this scan takes is unnoticeable. However, on a belaran@999: medium-sized tree (containing tens of thousands of files), it belaran@999: can take a second or more. belaran@999: belaran@999: The qpush and qpop commands allow you to push and belaran@999: pop multiple patches at a time. You can identify the belaran@999: destination patch that you want to end up at. belaran@999: When you qpush with a belaran@999: destination specified, it will push patches until that patch is belaran@999: at the top of the applied stack. When you qpop to a destination, MQ will pop belaran@999: patches until the destination patch is at the top. belaran@999: belaran@999: You can identify a destination patch using either the name belaran@999: of the patch, or by number. If you use numeric addressing, belaran@999: patches are counted from zero; this means that the first patch belaran@999: is zero, the second is one, and so on. belaran@999: belaran@999: belaran@999: belaran@999: Updating your patches when the underlying code belaran@999: changes belaran@999: belaran@999: It's common to have a stack of patches on top of an belaran@999: underlying repository that you don't modify directly. If you're belaran@999: working on changes to third-party code, or on a feature that is belaran@999: taking longer to develop than the rate of change of the code belaran@999: beneath, you will often need to sync up with the underlying belaran@999: code, and fix up any hunks in your patches that no longer apply. belaran@999: This is called rebasing your patch belaran@999: series. belaran@999: belaran@999: The simplest way to do this is to hg belaran@999: qpop your patches, then hg pull changes into the underlying belaran@999: repository, and finally hg qpush your belaran@999: patches again. MQ will stop pushing any time it runs across a belaran@999: patch that fails to apply during conflicts, allowing you to fix belaran@999: your conflicts, qrefresh the belaran@999: affected patch, and continue pushing until you have fixed your belaran@999: entire stack. belaran@999: belaran@999: This approach is easy to use and works well if you don't belaran@999: expect changes to the underlying code to affect how well your belaran@999: patches apply. If your patch stack touches code that is modified belaran@999: frequently or invasively in the underlying repository, however, belaran@999: fixing up rejected hunks by hand quickly becomes belaran@999: tiresome. belaran@999: belaran@999: It's possible to partially automate the rebasing process. belaran@999: If your patches apply cleanly against some revision of the belaran@999: underlying repo, MQ can use this information to help you to belaran@999: resolve conflicts between your patches and a different belaran@999: revision. belaran@999: belaran@999: The process is a little involved. belaran@999: belaran@999: To begin, hg qpush belaran@999: -a all of your patches on top of the revision belaran@999: where you know that they apply cleanly. belaran@999: belaran@999: Save a backup copy of your patch directory using belaran@999: hg qsave . belaran@999: This prints the name of the directory that it has saved the belaran@999: patches in. It will save the patches to a directory called belaran@999: .hg/patches.N, where belaran@999: N is a small integer. It also commits a belaran@999: save changeset on top of your applied belaran@999: patches; this is for internal book-keeping, and records the belaran@999: states of the series and belaran@999: status files. belaran@999: belaran@999: Use hg pull to belaran@999: bring new changes into the underlying repository. (Don't belaran@999: run hg pull -u; see below belaran@999: for why.) belaran@999: belaran@999: Update to the new tip revision, using hg update to override belaran@999: the patches you have pushed. belaran@999: belaran@999: Merge all patches using hg qpush -m belaran@999: -a. The option to belaran@999: qpush tells MQ to belaran@999: perform a three-way merge if the patch fails to belaran@999: apply. belaran@999: belaran@999: belaran@999: During the hg qpush , belaran@999: each patch in the series belaran@999: file is applied normally. If a patch applies with fuzz or belaran@999: rejects, MQ looks at the queue you qsaved, and performs a three-way belaran@999: merge with the corresponding changeset. This merge uses belaran@999: Mercurial's normal merge machinery, so it may pop up a GUI merge belaran@999: tool to help you to resolve problems. belaran@999: belaran@999: When you finish resolving the effects of a patch, MQ belaran@999: refreshes your patch based on the result of the merge. belaran@999: belaran@999: At the end of this process, your repository will have one belaran@999: extra head from the old patch queue, and a copy of the old patch belaran@999: queue will be in .hg/patches.N. You can remove the belaran@999: extra head using hg qpop -a -n belaran@999: patches.N or hg belaran@999: strip. You can delete .hg/patches.N once you are sure belaran@999: that you no longer need it as a backup. belaran@999: belaran@999: belaran@999: belaran@999: Identifying patches belaran@999: belaran@999: MQ commands that work with patches let you refer to a patch belaran@999: either by using its name or by a number. By name is obvious belaran@999: enough; pass the name foo.patch to qpush, for example, and it will belaran@999: push patches until foo.patch is belaran@999: applied. belaran@999: belaran@999: As a shortcut, you can refer to a patch using both a name belaran@999: and a numeric offset; foo.patch-2 means belaran@999: two patches before foo.patch, belaran@999: while bar.patch+4 means four patches belaran@999: after bar.patch. belaran@999: belaran@999: Referring to a patch by index isn't much different. The belaran@999: first patch printed in the output of qseries is patch zero (yes, it's belaran@999: one of those start-at-zero counting systems); the second is belaran@999: patch one; and so on. belaran@999: belaran@999: MQ also makes it easy to work with patches when you are belaran@999: using normal Mercurial commands. Every command that accepts a belaran@999: changeset ID will also accept the name of an applied patch. MQ belaran@999: augments the tags normally in the repository with an eponymous belaran@999: one for each applied patch. In addition, the special tags belaran@999: qbase and belaran@999: qtip identify belaran@999: the bottom-most and topmost applied patches, belaran@999: respectively. belaran@999: belaran@999: These additions to Mercurial's normal tagging capabilities belaran@999: make dealing with patches even more of a breeze. belaran@999: belaran@999: Want to patchbomb a mailing list with your belaran@999: latest series of changes? belaran@999: hg email qbase:qtip belaran@999: (Don't know what patchbombing is? See belaran@999: .) belaran@999: belaran@999: Need to see all of the patches since belaran@999: foo.patch that have touched files in a belaran@999: subdirectory of your tree? belaran@999: hg log -r foo.patch:qtip subdir belaran@999: belaran@999: belaran@999: belaran@999: Because MQ makes the names of patches available to the rest belaran@999: of Mercurial through its normal internal tag machinery, you belaran@999: don't need to type in the entire name of a patch when you want belaran@999: to identify it by name. belaran@999: belaran@999: Another nice consequence of representing patch names as tags belaran@999: is that when you run the hg log belaran@999: command, it will display a patch's name as a tag, simply as part belaran@999: of its normal output. This makes it easy to visually belaran@999: distinguish applied patches from underlying belaran@999: normal revisions. The following example shows a belaran@999: few normal Mercurial commands in use with applied belaran@999: patches. belaran@999: belaran@999: belaran@999: $ hg qapplied belaran@999: first.patch belaran@999: second.patch belaran@999: $ hg log -r qbase:qtip belaran@999: changeset: 1:c3bcf3b7335a belaran@999: tag: first.patch belaran@999: tag: qbase belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:08 2009 +0000 belaran@999: summary: [mq]: first.patch belaran@999: belaran@999: changeset: 2:d189ba63b5f7 belaran@999: tag: qtip belaran@999: tag: second.patch belaran@999: tag: tip belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:09 2009 +0000 belaran@999: summary: [mq]: second.patch belaran@999: belaran@999: $ hg export second.patch belaran@999: # HG changeset patch belaran@999: # User Bryan O'Sullivan <bos@serpentine.com> belaran@999: # Date 1250431509 0 belaran@999: # Node ID d189ba63b5f7427f9644663c01fc16fe80399c65 belaran@999: # Parent c3bcf3b7335afc0a250e85c51a1266d35d43a545 belaran@999: [mq]: second.patch belaran@999: belaran@999: diff -r c3bcf3b7335a -r d189ba63b5f7 other.c belaran@999: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 belaran@999: +++ b/other.c Sun Aug 16 14:05:09 2009 +0000 belaran@999: @@ -0,0 +1,1 @@ belaran@999: +double u; belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Useful things to know about belaran@999: belaran@999: There are a number of aspects of MQ usage that don't fit belaran@999: tidily into sections of their own, but that are good to know. belaran@999: Here they are, in one place. belaran@999: belaran@999: belaran@999: Normally, when you qpop a patch and qpush it again, the changeset belaran@999: that represents the patch after the pop/push will have a belaran@999: different identity than the changeset belaran@999: that represented the hash beforehand. See for belaran@999: information as to why this is. belaran@999: belaran@999: It's not a good idea to hg merge changes from another belaran@999: branch with a patch changeset, at least if you want to belaran@999: maintain the patchiness of that changeset and belaran@999: changesets below it on the patch stack. If you try to do belaran@999: this, it will appear to succeed, but MQ will become belaran@999: confused. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Managing patches in a repository belaran@999: belaran@999: Because MQ's .hg/patches directory resides belaran@999: outside a Mercurial repository's working directory, the belaran@999: underlying Mercurial repository knows nothing belaran@999: about the management or presence of patches. belaran@999: belaran@999: This presents the interesting possibility of managing the belaran@999: contents of the patch directory as a Mercurial repository in its belaran@999: own right. This can be a useful way to work. For example, you belaran@999: can work on a patch for a while, qrefresh it, then hg commit the current state of the belaran@999: patch. This lets you roll back to that version belaran@999: of the patch later on. belaran@999: belaran@999: You can then share different versions of the same patch belaran@999: stack among multiple underlying repositories. I use this when I belaran@999: am developing a Linux kernel feature. I have a pristine copy of belaran@999: my kernel sources for each of several CPU architectures, and a belaran@999: cloned repository under each that contains the patches I am belaran@999: working on. When I want to test a change on a different belaran@999: architecture, I push my current patches to the patch repository belaran@999: associated with that kernel tree, pop and push all of my belaran@999: patches, and build and test that kernel. belaran@999: belaran@999: Managing patches in a repository makes it possible for belaran@999: multiple developers to work on the same patch series without belaran@999: colliding with each other, all on top of an underlying source belaran@999: base that they may or may not control. belaran@999: belaran@999: belaran@999: MQ support for patch repositories belaran@999: belaran@999: MQ helps you to work with the .hg/patches directory as a belaran@999: repository; when you prepare a repository for working with belaran@999: patches using qinit, you belaran@999: can pass the option to create the .hg/patches directory as a belaran@999: Mercurial repository. belaran@999: belaran@999: belaran@999: If you forget to use the option, you belaran@999: can simply go into the .hg/patches directory at any belaran@999: time and run hg init. belaran@999: Don't forget to add an entry for the status file to the .hgignore file, though belaran@999: belaran@999: (hg qinit belaran@999: does this for you automatically); you belaran@999: really don't want to manage the belaran@999: status file. belaran@999: belaran@999: belaran@999: As a convenience, if MQ notices that the .hg/patches directory is a belaran@999: repository, it will automatically hg belaran@999: add every patch that you create and import. belaran@999: belaran@999: MQ provides a shortcut command, qcommit, that runs hg commit in the .hg/patches belaran@999: directory. This saves some bothersome typing. belaran@999: belaran@999: Finally, as a convenience to manage the patch directory, belaran@999: you can define the alias mq on Unix belaran@999: systems. For example, on Linux systems using the belaran@999: bash shell, you can include the following belaran@999: snippet in your ~/.bashrc. belaran@999: belaran@999: alias mq=`hg -R $(hg root)/.hg/patches' belaran@999: belaran@999: You can then issue commands of the form mq belaran@999: pull from the main repository. belaran@999: belaran@999: belaran@999: belaran@999: A few things to watch out for belaran@999: belaran@999: MQ's support for working with a repository full of patches belaran@999: is limited in a few small respects. belaran@999: belaran@999: MQ cannot automatically detect changes that you make to belaran@999: the patch directory. If you hg belaran@999: pull, manually edit, or hg belaran@999: update changes to patches or the series file, you will have to belaran@999: hg qpop and belaran@999: then hg qpush in belaran@999: the underlying repository to see those changes show up there. belaran@999: If you forget to do this, you can confuse MQ's idea of which belaran@999: patches are applied. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Third party tools for working with patches belaran@999: belaran@999: Once you've been working with patches for a while, you'll belaran@999: find yourself hungry for tools that will help you to understand belaran@999: and manipulate the patches you're dealing with. belaran@999: belaran@999: The diffstat command belaran@999: web:diffstat generates a histogram of the belaran@999: modifications made to each file in a patch. It provides a good belaran@999: way to get a sense of a patch—which files belaran@999: it affects, and how much change it introduces to each file and belaran@999: as a whole. (I find that it's a good idea to use belaran@999: diffstat's option as a matter of belaran@999: course, as otherwise it will try to do clever things with belaran@999: prefixes of file names that inevitably confuse at least belaran@999: me.) belaran@999: belaran@999: belaran@999: $ diffstat -p1 remove-redundant-null-checks.patch belaran@999: drivers/char/agp/sgi-agp.c | 5 ++--- belaran@999: drivers/char/hvcs.c | 11 +++++------ belaran@999: drivers/message/fusion/mptfc.c | 6 ++---- belaran@999: drivers/message/fusion/mptsas.c | 3 +-- belaran@999: drivers/net/fs_enet/fs_enet-mii.c | 3 +-- belaran@999: drivers/net/wireless/ipw2200.c | 22 ++++++---------------- belaran@999: drivers/scsi/libata-scsi.c | 4 +--- belaran@999: drivers/video/au1100fb.c | 3 +-- belaran@999: 8 files changed, 19 insertions(+), 38 deletions(-) belaran@999: $ filterdiff -i '*/video/*' remove-redundant-null-checks.patch belaran@999: --- a/drivers/video/au1100fb.c~remove-redundant-null-checks-before-free-in-drivers belaran@999: +++ a/drivers/video/au1100fb.c belaran@999: @@ -743,8 +743,7 @@ void __exit au1100fb_cleanup(void) belaran@999: { belaran@999: driver_unregister(&au1100fb_driver); belaran@999: belaran@999: - if (drv_info.opt_mode) belaran@999: - kfree(drv_info.opt_mode); belaran@999: + kfree(drv_info.opt_mode); belaran@999: } belaran@999: belaran@999: module_init(au1100fb_init); belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The patchutils package belaran@999: web:patchutils is invaluable. It provides a belaran@999: set of small utilities that follow the Unix belaran@999: philosophy; each does one useful thing with a patch. belaran@999: The patchutils command I use belaran@999: most is filterdiff, which extracts subsets belaran@999: from a patch file. For example, given a patch that modifies belaran@999: hundreds of files across dozens of directories, a single belaran@999: invocation of filterdiff can generate a belaran@999: smaller patch that only touches files whose names match a belaran@999: particular glob pattern. See for another belaran@999: example. belaran@999: belaran@999: belaran@999: belaran@999: Good ways to work with patches belaran@999: belaran@999: Whether you are working on a patch series to submit to a belaran@999: free software or open source project, or a series that you belaran@999: intend to treat as a sequence of regular changesets when you're belaran@999: done, you can use some simple techniques to keep your work well belaran@999: organized. belaran@999: belaran@999: Give your patches descriptive names. A good name for a belaran@999: patch might be rework-device-alloc.patch, belaran@999: because it will immediately give you a hint what the purpose of belaran@999: the patch is. Long names shouldn't be a problem; you won't be belaran@999: typing the names often, but you will be belaran@999: running commands like qapplied and qtop over and over. Good naming belaran@999: becomes especially important when you have a number of patches belaran@999: to work with, or if you are juggling a number of different tasks belaran@999: and your patches only get a fraction of your attention. belaran@999: belaran@999: Be aware of what patch you're working on. Use the qtop command and skim over the text belaran@999: of your patches frequently—for example, using hg tip )—to be sure belaran@999: of where you stand. I have several times worked on and qrefreshed a patch other than the belaran@999: one I intended, and it's often tricky to migrate changes into belaran@999: the right patch after making them in the wrong one. belaran@999: belaran@999: For this reason, it is very much worth investing a little belaran@999: time to learn how to use some of the third-party tools I belaran@999: described in , belaran@999: particularly belaran@999: diffstat and filterdiff. belaran@999: The former will give you a quick idea of what changes your patch belaran@999: is making, while the latter makes it easy to splice hunks belaran@999: selectively out of one patch and into another. belaran@999: belaran@999: belaran@999: belaran@999: MQ cookbook belaran@999: belaran@999: belaran@999: Manage <quote>trivial</quote> patches belaran@999: belaran@999: Because the overhead of dropping files into a new belaran@999: Mercurial repository is so low, it makes a lot of sense to belaran@999: manage patches this way even if you simply want to make a few belaran@999: changes to a source tarball that you downloaded. belaran@999: belaran@999: Begin by downloading and unpacking the source tarball, and belaran@999: turning it into a Mercurial repository. belaran@999: belaran@999: belaran@999: $ download netplug-1.2.5.tar.bz2 belaran@999: $ tar jxf netplug-1.2.5.tar.bz2 belaran@999: $ cd netplug-1.2.5 belaran@999: $ hg init belaran@999: $ hg commit -q --addremove --message netplug-1.2.5 belaran@999: $ cd .. belaran@999: $ hg clone netplug-1.2.5 netplug belaran@999: updating working directory belaran@999: 18 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Continue by creating a patch stack and making your belaran@999: changes. belaran@999: belaran@999: belaran@999: $ cd netplug belaran@999: $ hg qinit belaran@999: $ hg qnew -m 'fix build problem with gcc 4' build-fix.patch belaran@999: $ perl -pi -e 's/int addr_len/socklen_t addr_len/' netlink.c belaran@999: $ hg qrefresh belaran@999: $ hg tip -p belaran@999: changeset: 1:eeab56666c54 belaran@999: tag: qtip belaran@999: tag: build-fix.patch belaran@999: tag: tip belaran@999: tag: qbase belaran@999: user: Bryan O'Sullivan <bos@serpentine.com> belaran@999: date: Sun Aug 16 14:05:10 2009 +0000 belaran@999: summary: fix build problem with gcc 4 belaran@999: belaran@999: diff -r 1f6afe9a2d68 -r eeab56666c54 netlink.c belaran@999: --- a/netlink.c Sun Aug 16 14:05:09 2009 +0000 belaran@999: +++ b/netlink.c Sun Aug 16 14:05:10 2009 +0000 belaran@999: @@ -275,7 +275,7 @@ belaran@999: exit(1); belaran@999: } belaran@999: belaran@999: - int addr_len = sizeof(addr); belaran@999: + socklen_t addr_len = sizeof(addr); belaran@999: belaran@999: if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) { belaran@999: do_log(LOG_ERR, "Could not get socket details: %m"); belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Let's say a few weeks or months pass, and your package belaran@999: author releases a new version. First, bring their changes belaran@999: into the repository. belaran@999: belaran@999: belaran@999: $ hg qpop -a belaran@999: patch queue now empty belaran@999: $ cd .. belaran@999: $ download netplug-1.2.8.tar.bz2 belaran@999: $ hg clone netplug-1.2.5 netplug-1.2.8 belaran@999: updating working directory belaran@999: 18 files updated, 0 files merged, 0 files removed, 0 files unresolved belaran@999: $ cd netplug-1.2.8 belaran@999: $ hg locate -0 | xargs -0 rm belaran@999: $ cd .. belaran@999: $ tar jxf netplug-1.2.8.tar.bz2 belaran@999: $ cd netplug-1.2.8 belaran@999: $ hg commit --addremove --message netplug-1.2.8 belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The pipeline starting with hg belaran@999: locate above deletes all files in the working belaran@999: directory, so that hg belaran@999: commit's option can belaran@999: actually tell which files have really been removed in the belaran@999: newer version of the source. belaran@999: belaran@999: Finally, you can apply your patches on top of the new belaran@999: tree. belaran@999: belaran@999: belaran@999: $ cd ../netplug belaran@999: $ hg pull ../netplug-1.2.8 belaran@999: pulling from ../netplug-1.2.8 belaran@999: searching for changes belaran@999: adding changesets belaran@999: adding manifests belaran@999: adding file changes belaran@999: added 1 changesets with 12 changes to 12 files belaran@999: (run 'hg update' to get a working copy) belaran@999: $ hg qpush -a belaran@999: (working directory not at tip) belaran@999: applying build-fix.patch belaran@999: now at: build-fix.patch belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Combining entire patches belaran@999: belaran@999: MQ provides a command, qfold that lets you combine belaran@999: entire patches. This folds the patches you belaran@999: name, in the order you name them, into the topmost applied belaran@999: patch, and concatenates their descriptions onto the end of its belaran@999: description. The patches that you fold must be unapplied belaran@999: before you fold them. belaran@999: belaran@999: The order in which you fold patches matters. If your belaran@999: topmost applied patch is foo, and you belaran@999: qfold belaran@999: bar and quux into it, belaran@999: you will end up with a patch that has the same effect as if belaran@999: you applied first foo, then belaran@999: bar, followed by belaran@999: quux. belaran@999: belaran@999: belaran@999: belaran@999: Merging part of one patch into another belaran@999: belaran@999: Merging part of one patch into belaran@999: another is more difficult than combining entire belaran@999: patches. belaran@999: belaran@999: If you want to move changes to entire files, you can use belaran@999: filterdiff's and options to choose the belaran@999: modifications to snip out of one patch, concatenating its belaran@999: output onto the end of the patch you want to merge into. You belaran@999: usually won't need to modify the patch you've merged the belaran@999: changes from. Instead, MQ will report some rejected hunks belaran@999: when you qpush it (from belaran@999: the hunks you moved into the other patch), and you can simply belaran@999: qrefresh the patch to drop belaran@999: the duplicate hunks. belaran@999: belaran@999: If you have a patch that has multiple hunks modifying a belaran@999: file, and you only want to move a few of those hunks, the job belaran@999: becomes more messy, but you can still partly automate it. Use belaran@999: lsdiff -nvv to print some metadata about belaran@999: the patch. belaran@999: belaran@999: belaran@999: $ lsdiff -nvv remove-redundant-null-checks.patch belaran@999: 22 File #1 a/drivers/char/agp/sgi-agp.c belaran@999: 24 Hunk #1 static int __devinit agp_sgi_init(void) belaran@999: 37 File #2 a/drivers/char/hvcs.c belaran@999: 39 Hunk #1 static struct tty_operations hvcs_ops = belaran@999: 53 Hunk #2 static int hvcs_alloc_index_list(int n) belaran@999: 69 File #3 a/drivers/message/fusion/mptfc.c belaran@999: 71 Hunk #1 mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in belaran@999: 85 File #4 a/drivers/message/fusion/mptsas.c belaran@999: 87 Hunk #1 mptsas_probe_hba_phys(MPT_ADAPTER *ioc) belaran@999: 98 File #5 a/drivers/net/fs_enet/fs_enet-mii.c belaran@999: 100 Hunk #1 static struct fs_enet_mii_bus *create_bu belaran@999: 111 File #6 a/drivers/net/wireless/ipw2200.c belaran@999: 113 Hunk #1 static struct ipw_fw_error *ipw_alloc_er belaran@999: 126 Hunk #2 static ssize_t clear_error(struct device belaran@999: 140 Hunk #3 static void ipw_irq_tasklet(struct ipw_p belaran@999: 150 Hunk #4 static void ipw_pci_remove(struct pci_de belaran@999: 164 File #7 a/drivers/scsi/libata-scsi.c belaran@999: 166 Hunk #1 int ata_cmd_ioctl(struct scsi_device *sc belaran@999: 178 File #8 a/drivers/video/au1100fb.c belaran@999: 180 Hunk #1 void __exit au1100fb_cleanup(void) belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This command prints three different kinds of belaran@999: number: belaran@999: belaran@999: (in the first column) a file belaran@999: number to identify each file modified in the belaran@999: patch; belaran@999: belaran@999: (on the next line, indented) the line number belaran@999: within a modified file where a hunk starts; and belaran@999: belaran@999: (on the same line) a hunk belaran@999: number to identify that hunk. belaran@999: belaran@999: belaran@999: You'll have to use some visual inspection, and reading of belaran@999: the patch, to identify the file and hunk numbers you'll want, belaran@999: but you can then pass them to to belaran@999: filterdiff's and options, to belaran@999: select exactly the file and hunk you want to extract. belaran@999: belaran@999: Once you have this hunk, you can concatenate it onto the belaran@999: end of your destination patch and continue with the remainder belaran@999: of . belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Differences between quilt and MQ belaran@999: belaran@999: If you are already familiar with quilt, MQ provides a belaran@999: similar command set. There are a few differences in the way belaran@999: that it works. belaran@999: belaran@999: You will already have noticed that most quilt commands have belaran@999: MQ counterparts that simply begin with a belaran@999: q. The exceptions are quilt's belaran@999: add and remove commands, belaran@999: the counterparts for which are the normal Mercurial hg add and hg belaran@999: remove commands. There is no MQ equivalent of the belaran@999: quilt edit command. belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Advanced uses of Mercurial Queues belaran@999: belaran@999: While it's easy to pick up straightforward uses of Mercurial belaran@999: Queues, use of a little discipline and some of MQ's less belaran@999: frequently used capabilities makes it possible to work in belaran@999: complicated development environments. belaran@999: belaran@999: In this chapter, I will use as an example a technique I have belaran@999: used to manage the development of an Infiniband device driver for belaran@999: the Linux kernel. The driver in question is large (at least as belaran@999: drivers go), with 25,000 lines of code spread across 35 source belaran@999: files. It is maintained by a small team of developers. belaran@999: belaran@999: While much of the material in this chapter is specific to belaran@999: Linux, the same principles apply to any code base for which you're belaran@999: not the primary owner, and upon which you need to do a lot of belaran@999: development. belaran@999: belaran@999: belaran@999: The problem of many targets belaran@999: belaran@999: The Linux kernel changes rapidly, and has never been belaran@999: internally stable; developers frequently make drastic changes belaran@999: between releases. This means that a version of the driver that belaran@999: works well with a particular released version of the kernel will belaran@999: not even compile correctly against, belaran@999: typically, any other version. belaran@999: belaran@999: To maintain a driver, we have to keep a number of distinct belaran@999: versions of Linux in mind. belaran@999: belaran@999: One target is the main Linux kernel development belaran@999: tree. Maintenance of the code is in this case partly shared belaran@999: by other developers in the kernel community, who make belaran@999: drive-by modifications to the driver as they belaran@999: develop and refine kernel subsystems. belaran@999: belaran@999: We also maintain a number of belaran@999: backports to older versions of the Linux belaran@999: kernel, to support the needs of customers who are running belaran@999: older Linux distributions that do not incorporate our belaran@999: drivers. (To backport a piece of code belaran@999: is to modify it to work in an older version of its target belaran@999: environment than the version it was developed for.) belaran@999: belaran@999: Finally, we make software releases on a schedule belaran@999: that is necessarily not aligned with those used by Linux belaran@999: distributors and kernel developers, so that we can deliver belaran@999: new features to customers without forcing them to upgrade belaran@999: their entire kernels or distributions. belaran@999: belaran@999: belaran@999: belaran@999: Tempting approaches that don't work well belaran@999: belaran@999: There are two standard ways to maintain a belaran@999: piece of software that has to target many different belaran@999: environments. belaran@999: belaran@999: The first is to maintain a number of branches, each belaran@999: intended for a single target. The trouble with this approach belaran@999: is that you must maintain iron discipline in the flow of belaran@999: changes between repositories. A new feature or bug fix must belaran@999: start life in a pristine repository, then belaran@999: percolate out to every backport repository. Backport changes belaran@999: are more limited in the branches they should propagate to; a belaran@999: backport change that is applied to a branch where it doesn't belaran@999: belong will probably stop the driver from compiling. belaran@999: belaran@999: The second is to maintain a single source tree filled with belaran@999: conditional statements that turn chunks of code on or off belaran@999: depending on the intended target. Because these belaran@999: ifdefs are not allowed in the Linux kernel belaran@999: tree, a manual or automatic process must be followed to strip belaran@999: them out and yield a clean tree. A code base maintained in belaran@999: this fashion rapidly becomes a rat's nest of conditional belaran@999: blocks that are difficult to understand and maintain. belaran@999: belaran@999: Neither of these approaches is well suited to a situation belaran@999: where you don't own the canonical copy of a belaran@999: source tree. In the case of a Linux driver that is belaran@999: distributed with the standard kernel, Linus's tree contains belaran@999: the copy of the code that will be treated by the world as belaran@999: canonical. The upstream version of my driver belaran@999: can be modified by people I don't know, without me even belaran@999: finding out about it until after the changes show up in belaran@999: Linus's tree. belaran@999: belaran@999: These approaches have the added weakness of making it belaran@999: difficult to generate well-formed patches to submit belaran@999: upstream. belaran@999: belaran@999: In principle, Mercurial Queues seems like a good candidate belaran@999: to manage a development scenario such as the above. While belaran@999: this is indeed the case, MQ contains a few added features that belaran@999: make the job more pleasant. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Conditionally applying patches with guards belaran@999: belaran@999: Perhaps the best way to maintain sanity with so many targets belaran@999: is to be able to choose specific patches to apply for a given belaran@999: situation. MQ provides a feature called guards belaran@999: (which originates with quilt's guards belaran@999: command) that does just this. To start off, let's create a belaran@999: simple repository for experimenting in. belaran@999: belaran@999: belaran@999: $ hg qinit belaran@999: $ hg qnew hello.patch belaran@999: $ echo hello > hello belaran@999: $ hg add hello belaran@999: $ hg qrefresh belaran@999: $ hg qnew goodbye.patch belaran@999: $ echo goodbye > goodbye belaran@999: $ hg add goodbye belaran@999: $ hg qrefresh belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: This gives us a tiny repository that contains two patches belaran@999: that don't have any dependencies on each other, because they belaran@999: touch different files. belaran@999: belaran@999: The idea behind conditional application is that you can belaran@999: tag a patch with a guard, belaran@999: which is simply a text string of your choosing, then tell MQ to belaran@999: select specific guards to use when applying patches. MQ will belaran@999: then either apply, or skip over, a guarded patch, depending on belaran@999: the guards that you have selected. belaran@999: belaran@999: A patch can have an arbitrary number of guards; each one is belaran@999: positive (apply this patch if this belaran@999: guard is selected) or negative belaran@999: (skip this patch if this guard is selected). A belaran@999: patch with no guards is always applied. belaran@999: belaran@999: belaran@999: belaran@999: Controlling the guards on a patch belaran@999: belaran@999: The qguard command lets belaran@999: you determine which guards should apply to a patch, or display belaran@999: the guards that are already in effect. Without any arguments, it belaran@999: displays the guards on the current topmost patch. belaran@999: belaran@999: belaran@999: $ hg qguard belaran@999: goodbye.patch: unguarded belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: To set a positive guard on a patch, prefix the name of the belaran@999: guard with a +. belaran@999: belaran@999: belaran@999: $ hg qguard +foo belaran@999: $ hg qguard belaran@999: goodbye.patch: +foo belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: To set a negative guard belaran@999: on a patch, prefix the name of the guard with a belaran@999: -. belaran@999: belaran@999: belaran@999: $ hg qguard -- hello.patch -quux belaran@999: $ hg qguard hello.patch belaran@999: hello.patch: -quux belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Notice that we prefixed the arguments to the hg belaran@999: qguard command with a -- here, so belaran@999: that Mercurial would not interpret the text belaran@999: -quux as an option. belaran@999: belaran@999: belaran@999: Setting vs. modifying belaran@999: belaran@999: The qguard command belaran@999: sets the guards on a patch; it doesn't belaran@999: modify them. What this means is that if belaran@999: you run hg qguard +a +b on a belaran@999: patch, then hg qguard +c on belaran@999: the same patch, the only guard that will belaran@999: be set on it afterwards is +c. belaran@999: belaran@999: belaran@999: Mercurial stores guards in the series file; the form in which they belaran@999: are stored is easy both to understand and to edit by hand. (In belaran@999: other words, you don't have to use the qguard command if you don't want belaran@999: to; it's okay to simply edit the series file.) belaran@999: belaran@999: belaran@999: $ cat .hg/patches/series belaran@999: hello.patch #-quux belaran@999: goodbye.patch #+foo belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Selecting the guards to use belaran@999: belaran@999: The qselect command belaran@999: determines which guards are active at a given time. The effect belaran@999: of this is to determine which patches MQ will apply the next belaran@999: time you run qpush. It has belaran@999: no other effect; in particular, it doesn't do anything to belaran@999: patches that are already applied. belaran@999: belaran@999: With no arguments, the qselect command lists the guards belaran@999: currently in effect, one per line of output. Each argument is belaran@999: treated as the name of a guard to apply. belaran@999: belaran@999: belaran@999: $ hg qpop -a belaran@999: patch queue now empty belaran@999: $ hg qselect belaran@999: no active guards belaran@999: $ hg qselect foo belaran@999: number of unguarded, unapplied patches has changed from 1 to 2 belaran@999: $ hg qselect belaran@999: foo belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: In case you're interested, the currently selected guards are belaran@999: stored in the guards file. belaran@999: belaran@999: belaran@999: $ cat .hg/patches/guards belaran@999: foo belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: We can see the effect the selected guards have when we run belaran@999: qpush. belaran@999: belaran@999: belaran@999: $ hg qpush -a belaran@999: applying hello.patch belaran@999: applying goodbye.patch belaran@999: now at: goodbye.patch belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: A guard cannot start with a belaran@999: + or belaran@999: - character. The name of a belaran@999: guard must not contain white space, but most other characters belaran@999: are acceptable. If you try to use a guard with an invalid name, belaran@999: MQ will complain: belaran@999: belaran@999: belaran@999: $ hg qselect +foo belaran@999: abort: guard '+foo' starts with invalid character: '+' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Changing the selected guards changes the patches that are belaran@999: applied. belaran@999: belaran@999: belaran@999: $ hg qselect quux belaran@999: number of guarded, applied patches has changed from 0 to 2 belaran@999: $ hg qpop -a belaran@999: patch queue now empty belaran@999: $ hg qpush -a belaran@999: patch series already fully applied belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: You can see in the example below that negative guards take belaran@999: precedence over positive guards. belaran@999: belaran@999: belaran@999: $ hg qselect foo bar belaran@999: number of unguarded, unapplied patches has changed from 0 to 2 belaran@999: $ hg qpop -a belaran@999: no patches applied belaran@999: $ hg qpush -a belaran@999: applying hello.patch belaran@999: applying goodbye.patch belaran@999: now at: goodbye.patch belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: MQ's rules for applying patches belaran@999: belaran@999: The rules that MQ uses when deciding whether to apply a belaran@999: patch are as follows. belaran@999: belaran@999: A patch that has no guards is always belaran@999: applied. belaran@999: belaran@999: If the patch has any negative guard that matches belaran@999: any currently selected guard, the patch is skipped. belaran@999: belaran@999: If the patch has any positive guard that matches belaran@999: any currently selected guard, the patch is applied. belaran@999: belaran@999: If the patch has positive or negative guards, belaran@999: but none matches any currently selected guard, the patch is belaran@999: skipped. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Trimming the work environment belaran@999: belaran@999: In working on the device driver I mentioned earlier, I don't belaran@999: apply the patches to a normal Linux kernel tree. Instead, I use belaran@999: a repository that contains only a snapshot of the source files belaran@999: and headers that are relevant to Infiniband development. This belaran@999: repository is 1% the size of a kernel repository, so it's easier belaran@999: to work with. belaran@999: belaran@999: I then choose a base version on top of which belaran@999: the patches are applied. This is a snapshot of the Linux kernel belaran@999: tree as of a revision of my choosing. When I take the snapshot, belaran@999: I record the changeset ID from the kernel repository in the belaran@999: commit message. Since the snapshot preserves the belaran@999: shape and content of the relevant parts of the belaran@999: kernel tree, I can apply my patches on top of either my tiny belaran@999: repository or a normal kernel tree. belaran@999: belaran@999: Normally, the base tree atop which the patches apply should belaran@999: be a snapshot of a very recent upstream tree. This best belaran@999: facilitates the development of patches that can easily be belaran@999: submitted upstream with few or no modifications. belaran@999: belaran@999: belaran@999: belaran@999: Dividing up the <filename role="special" moreinfo="none">series</filename> belaran@999: file belaran@999: belaran@999: I categorise the patches in the series file into a number of logical belaran@999: groups. Each section of like patches begins with a block of belaran@999: comments that describes the purpose of the patches that belaran@999: follow. belaran@999: belaran@999: The sequence of patch groups that I maintain follows. The belaran@999: ordering of these groups is important; I'll describe why after I belaran@999: introduce the groups. belaran@999: belaran@999: The accepted group. Patches that belaran@999: the development team has submitted to the maintainer of the belaran@999: Infiniband subsystem, and which he has accepted, but which belaran@999: are not present in the snapshot that the tiny repository is belaran@999: based on. These are read only patches, belaran@999: present only to transform the tree into a similar state as belaran@999: it is in the upstream maintainer's repository. belaran@999: belaran@999: The rework group. Patches that I belaran@999: have submitted, but that the upstream maintainer has belaran@999: requested modifications to before he will accept belaran@999: them. belaran@999: belaran@999: The pending group. Patches that belaran@999: I have not yet submitted to the upstream maintainer, but belaran@999: which we have finished working on. These will be read belaran@999: only for a while. If the upstream maintainer belaran@999: accepts them upon submission, I'll move them to the end of belaran@999: the accepted group. If he requests that I belaran@999: modify any, I'll move them to the beginning of the belaran@999: rework group. belaran@999: belaran@999: The in progress group. Patches belaran@999: that are actively being developed, and should not be belaran@999: submitted anywhere yet. belaran@999: belaran@999: The backport group. Patches that belaran@999: adapt the source tree to older versions of the kernel belaran@999: tree. belaran@999: belaran@999: The do not ship group. Patches belaran@999: that for some reason should never be submitted upstream. belaran@999: For example, one such patch might change embedded driver belaran@999: identification strings to make it easier to distinguish, in belaran@999: the field, between an out-of-tree version of the driver and belaran@999: a version shipped by a distribution vendor. belaran@999: belaran@999: belaran@999: Now to return to the reasons for ordering groups of patches belaran@999: in this way. We would like the lowest patches in the stack to belaran@999: be as stable as possible, so that we will not need to rework belaran@999: higher patches due to changes in context. Putting patches that belaran@999: will never be changed first in the series file serves this belaran@999: purpose. belaran@999: belaran@999: We would also like the patches that we know we'll need to belaran@999: modify to be applied on top of a source tree that resembles the belaran@999: upstream tree as closely as possible. This is why we keep belaran@999: accepted patches around for a while. belaran@999: belaran@999: The backport and do not ship belaran@999: patches float at the end of the series file. The backport patches belaran@999: must be applied on top of all other patches, and the do belaran@999: not ship patches might as well stay out of harm's belaran@999: way. belaran@999: belaran@999: belaran@999: belaran@999: Maintaining the patch series belaran@999: belaran@999: In my work, I use a number of guards to control which belaran@999: patches are to be applied. belaran@999: belaran@999: belaran@999: Accepted patches are guarded with belaran@999: accepted. I enable this guard most of belaran@999: the time. When I'm applying the patches on top of a tree belaran@999: where the patches are already present, I can turn this patch belaran@999: off, and the patches that follow it will apply belaran@999: cleanly. belaran@999: belaran@999: Patches that are finished, but belaran@999: not yet submitted, have no guards. If I'm applying the belaran@999: patch stack to a copy of the upstream tree, I don't need to belaran@999: enable any guards in order to get a reasonably safe source belaran@999: tree. belaran@999: belaran@999: Those patches that need reworking before being belaran@999: resubmitted are guarded with belaran@999: rework. belaran@999: belaran@999: For those patches that are still under belaran@999: development, I use devel. belaran@999: belaran@999: A backport patch may have several guards, one belaran@999: for each version of the kernel to which it applies. For belaran@999: example, a patch that backports a piece of code to 2.6.9 belaran@999: will have a 2.6.9 guard. belaran@999: belaran@999: This variety of guards gives me considerable flexibility in belaran@999: determining what kind of source tree I want to end up with. For belaran@999: most situations, the selection of appropriate guards is belaran@999: automated during the build process, but I can manually tune the belaran@999: guards to use for less common circumstances. belaran@999: belaran@999: belaran@999: The art of writing backport patches belaran@999: belaran@999: Using MQ, writing a backport patch is a simple process. belaran@999: All such a patch has to do is modify a piece of code that uses belaran@999: a kernel feature not present in the older version of the belaran@999: kernel, so that the driver continues to work correctly under belaran@999: that older version. belaran@999: belaran@999: A useful goal when writing a good backport patch is to belaran@999: make your code look as if it was written for the older version belaran@999: of the kernel you're targeting. The less obtrusive the patch, belaran@999: the easier it will be to understand and maintain. If you're belaran@999: writing a collection of backport patches to avoid the belaran@999: rat's nest effect of lots of belaran@999: #ifdefs (hunks of source code that are only belaran@999: used conditionally) in your code, don't introduce belaran@999: version-dependent #ifdefs into the patches. belaran@999: Instead, write several patches, each of which makes belaran@999: unconditional changes, and control their application using belaran@999: guards. belaran@999: belaran@999: There are two reasons to divide backport patches into a belaran@999: distinct group, away from the regular patches belaran@999: whose effects they modify. The first is that intermingling the belaran@999: two makes it more difficult to use a tool like the patchbomb extension to automate the belaran@999: process of submitting the patches to an upstream maintainer. belaran@999: The second is that a backport patch could perturb the context belaran@999: in which a subsequent regular patch is applied, making it belaran@999: impossible to apply the regular patch cleanly belaran@999: without the earlier backport patch belaran@999: already being applied. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Useful tips for developing with MQ belaran@999: belaran@999: belaran@999: Organising patches in directories belaran@999: belaran@999: If you're working on a substantial project with MQ, it's belaran@999: not difficult to accumulate a large number of patches. For belaran@999: example, I have one patch repository that contains over 250 belaran@999: patches. belaran@999: belaran@999: If you can group these patches into separate logical belaran@999: categories, you can if you like store them in different belaran@999: directories; MQ has no problems with patch names that contain belaran@999: path separators. belaran@999: belaran@999: belaran@999: belaran@999: Viewing the history of a patch belaran@999: belaran@999: If you're developing a set of patches over a long time, belaran@999: it's a good idea to maintain them in a repository, as belaran@999: discussed in . If you do belaran@999: so, you'll quickly belaran@999: discover that using the hg belaran@999: diff command to look at the history of changes to belaran@999: a patch is unworkable. This is in part because you're looking belaran@999: at the second derivative of the real code (a diff of a diff), belaran@999: but also because MQ adds noise to the process by modifying belaran@999: time stamps and directory names when it updates a belaran@999: patch. belaran@999: belaran@999: However, you can use the extdiff extension, which is bundled belaran@999: with Mercurial, to turn a diff of two versions of a patch into belaran@999: something readable. To do this, you will need a third-party belaran@999: package called patchutils belaran@999: web:patchutils. This provides a command belaran@999: named interdiff, which shows the belaran@999: differences between two diffs as a diff. Used on two versions belaran@999: of the same diff, it generates a diff that represents the diff belaran@999: from the first to the second version. belaran@999: belaran@999: You can enable the extdiff extension in the usual way, belaran@999: by adding a line to the extensions section of your belaran@999: ~/.hgrc. belaran@999: [extensions] belaran@999: extdiff = belaran@999: The interdiff command expects to be belaran@999: passed the names of two files, but the extdiff extension passes the program belaran@999: it runs a pair of directories, each of which can contain an belaran@999: arbitrary number of files. We thus need a small program that belaran@999: will run interdiff on each pair of files in belaran@999: these two directories. This program is available as hg-interdiff in the examples directory of the belaran@999: source code repository that accompanies this book. belaran@999: belaran@999: With the hg-interdiff belaran@999: program in your shell's search path, you can run it as belaran@999: follows, from inside an MQ patch directory: belaran@999: hg extdiff -p hg-interdiff -r A:B my-change.patch belaran@999: Since you'll probably want to use this long-winded command belaran@999: a lot, you can get hgext to belaran@999: make it available as a normal Mercurial command, again by belaran@999: editing your ~/.hgrc. belaran@999: [extdiff] belaran@999: cmd.interdiff = hg-interdiff belaran@999: This directs hgext to belaran@999: make an interdiff command available, so you belaran@999: can now shorten the previous invocation of extdiff to something a belaran@999: little more wieldy. belaran@999: hg interdiff -r A:B my-change.patch belaran@999: belaran@999: belaran@999: The interdiff command works well belaran@999: only if the underlying files against which versions of a belaran@999: patch are generated remain the same. If you create a patch, belaran@999: modify the underlying files, and then regenerate the patch, belaran@999: interdiff may not produce useful belaran@999: output. belaran@999: belaran@999: belaran@999: The extdiff extension is belaran@999: useful for more than merely improving the presentation of MQ belaran@999: patches. To read more about it, go to . belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Adding functionality with extensions belaran@999: belaran@999: While the core of Mercurial is quite complete from a belaran@999: functionality standpoint, it's deliberately shorn of fancy belaran@999: features. This approach of preserving simplicity keeps the belaran@999: software easy to deal with for both maintainers and users. belaran@999: belaran@999: However, Mercurial doesn't box you in with an inflexible belaran@999: command set: you can add features to it as belaran@999: extensions (sometimes known as belaran@999: plugins). We've already discussed a few of belaran@999: these extensions in earlier chapters. belaran@999: belaran@999: belaran@999: covers the fetch extension; belaran@999: this combines pulling new changes and merging them with local belaran@999: changes into a single command, fetch. belaran@999: belaran@999: In , we covered belaran@999: several extensions that are useful for hook-related belaran@999: functionality: acl adds belaran@999: access control lists; bugzilla adds integration with the belaran@999: Bugzilla bug tracking system; and notify sends notification emails on belaran@999: new changes. belaran@999: belaran@999: The Mercurial Queues patch management extension is belaran@999: so invaluable that it merits two chapters and an appendix all belaran@999: to itself. covers the belaran@999: basics; discusses advanced topics; belaran@999: and goes into detail on belaran@999: each belaran@999: command. belaran@999: belaran@999: belaran@999: In this chapter, we'll cover some of the other extensions that belaran@999: are available for Mercurial, and briefly touch on some of the belaran@999: machinery you'll need to know about if you want to write an belaran@999: extension of your own. belaran@999: belaran@999: In , belaran@999: we'll discuss the possibility of huge belaran@999: performance improvements using the inotify extension. belaran@999: belaran@999: belaran@999: belaran@999: Improve performance with the <literal role="hg-ext" moreinfo="none">inotify</literal> extension belaran@999: belaran@999: Are you interested in having some of the most common belaran@999: Mercurial operations run as much as a hundred times faster? belaran@999: Read on! belaran@999: belaran@999: Mercurial has great performance under normal circumstances. belaran@999: For example, when you run the hg belaran@999: status command, Mercurial has to scan almost every belaran@999: directory and file in your repository so that it can display belaran@999: file status. Many other Mercurial commands need to do the same belaran@999: work behind the scenes; for example, the hg diff command uses the status belaran@999: machinery to avoid doing an expensive comparison operation on belaran@999: files that obviously haven't changed. belaran@999: belaran@999: Because obtaining file status is crucial to good belaran@999: performance, the authors of Mercurial have optimised this code belaran@999: to within an inch of its life. However, there's no avoiding the belaran@999: fact that when you run hg belaran@999: status, Mercurial is going to have to perform at belaran@999: least one expensive system call for each managed file to belaran@999: determine whether it's changed since the last time Mercurial belaran@999: checked. For a sufficiently large repository, this can take a belaran@999: long time. belaran@999: belaran@999: To put a number on the magnitude of this effect, I created a belaran@999: repository containing 150,000 managed files. I timed hg status as taking ten seconds to belaran@999: run, even when none of those files had been belaran@999: modified. belaran@999: belaran@999: Many modern operating systems contain a file notification belaran@999: facility. If a program signs up to an appropriate service, the belaran@999: operating system will notify it every time a file of interest is belaran@999: created, modified, or deleted. On Linux systems, the kernel belaran@999: component that does this is called belaran@999: inotify. belaran@999: belaran@999: Mercurial's inotify belaran@999: extension talks to the kernel's inotify belaran@999: component to optimise hg status belaran@999: commands. The extension has two components. A daemon sits in belaran@999: the background and receives notifications from the belaran@999: inotify subsystem. It also listens for belaran@999: connections from a regular Mercurial command. The extension belaran@999: modifies Mercurial's behavior so that instead of scanning the belaran@999: filesystem, it queries the daemon. Since the daemon has perfect belaran@999: information about the state of the repository, it can respond belaran@999: with a result instantaneously, avoiding the need to scan every belaran@999: directory and file in the repository. belaran@999: belaran@999: Recall the ten seconds that I measured plain Mercurial as belaran@999: taking to run hg status on a belaran@999: 150,000 file repository. With the inotify extension enabled, the time belaran@999: dropped to 0.1 seconds, a factor of one belaran@999: hundred faster. belaran@999: belaran@999: Before we continue, please pay attention to some belaran@999: caveats. belaran@999: belaran@999: The inotify belaran@999: extension is Linux-specific. Because it interfaces directly belaran@999: to the Linux kernel's inotify subsystem, belaran@999: it does not work on other operating systems. belaran@999: belaran@999: It should work on any Linux distribution that belaran@999: was released after early 2005. Older distributions are belaran@999: likely to have a kernel that lacks belaran@999: inotify, or a version of belaran@999: glibc that does not have the necessary belaran@999: interfacing support. belaran@999: belaran@999: Not all filesystems are suitable for use with belaran@999: the inotify extension. belaran@999: Network filesystems such as NFS are a non-starter, for belaran@999: example, particularly if you're running Mercurial on several belaran@999: systems, all mounting the same network filesystem. The belaran@999: kernel's inotify system has no way of belaran@999: knowing about changes made on another system. Most local belaran@999: filesystems (e.g. ext3, XFS, ReiserFS) should work belaran@999: fine. belaran@999: belaran@999: belaran@999: The inotify extension is belaran@999: not yet shipped with Mercurial as of May 2007, so it's a little belaran@999: more involved to set up than other extensions. But the belaran@999: performance improvement is worth it! belaran@999: belaran@999: The extension currently comes in two parts: a set of patches belaran@999: to the Mercurial source code, and a library of Python bindings belaran@999: to the inotify subsystem. belaran@999: belaran@999: There are two Python belaran@999: inotify binding libraries. One of them is belaran@999: called pyinotify, and is packaged by some belaran@999: Linux distributions as python-inotify. belaran@999: This is not the one you'll need, as it is belaran@999: too buggy and inefficient to be practical. belaran@999: belaran@999: To get going, it's best to already have a functioning copy belaran@999: of Mercurial installed. belaran@999: belaran@999: If you follow the instructions below, you'll be belaran@999: replacing and overwriting any existing belaran@999: installation of Mercurial that you might already have, using belaran@999: the latest bleeding edge Mercurial code. Don't belaran@999: say you weren't warned! belaran@999: belaran@999: belaran@999: Clone the Python inotify belaran@999: binding repository. Build and install it. belaran@999: hg clone http://hg.kublai.com/python/inotify belaran@999: cd inotify belaran@999: python setup.py build --force belaran@999: sudo python setup.py install --skip-build belaran@999: belaran@999: Clone the crew Mercurial repository. belaran@999: Clone the inotify patch belaran@999: repository so that Mercurial Queues will be able to apply belaran@999: patches to your cope of the crew repository. belaran@999: hg clone http://hg.intevation.org/mercurial/crew belaran@999: hg clone crew inotify belaran@999: hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches belaran@999: belaran@999: Make sure that you have the Mercurial Queues belaran@999: extension, mq, enabled. If belaran@999: you've never used MQ, read to get started belaran@999: quickly. belaran@999: belaran@999: Go into the inotify repo, and apply all belaran@999: of the inotify patches belaran@999: using the option to the qpush command. belaran@999: cd inotify belaran@999: hg qpush -a belaran@999: belaran@999: If you get an error message from qpush, you should not continue. belaran@999: Instead, ask for help. belaran@999: belaran@999: Build and install the patched version of belaran@999: Mercurial. belaran@999: python setup.py build --force belaran@999: sudo python setup.py install --skip-build belaran@999: belaran@999: belaran@999: Once you've build a suitably patched version of Mercurial, belaran@999: all you need to do to enable the inotify extension is add an entry to belaran@999: your ~/.hgrc. belaran@999: [extensions] inotify = belaran@999: When the inotify extension belaran@999: is enabled, Mercurial will automatically and transparently start belaran@999: the status daemon the first time you run a command that needs belaran@999: status in a repository. It runs one status daemon per belaran@999: repository. belaran@999: belaran@999: The status daemon is started silently, and runs in the belaran@999: background. If you look at a list of running processes after belaran@999: you've enabled the inotify belaran@999: extension and run a few commands in different repositories, belaran@999: you'll thus see a few hg processes sitting belaran@999: around, waiting for updates from the kernel and queries from belaran@999: Mercurial. belaran@999: belaran@999: The first time you run a Mercurial command in a repository belaran@999: when you have the inotify belaran@999: extension enabled, it will run with about the same performance belaran@999: as a normal Mercurial command. This is because the status belaran@999: daemon needs to perform a normal status scan so that it has a belaran@999: baseline against which to apply later updates from the kernel. belaran@999: However, every subsequent command that does belaran@999: any kind of status check should be noticeably faster on belaran@999: repositories of even fairly modest size. Better yet, the bigger belaran@999: your repository is, the greater a performance advantage you'll belaran@999: see. The inotify daemon makes belaran@999: status operations almost instantaneous on repositories of all belaran@999: sizes! belaran@999: belaran@999: If you like, you can manually start a status daemon using belaran@999: the inserve command. belaran@999: This gives you slightly finer control over how the daemon ought belaran@999: to run. This command will of course only be available when the belaran@999: inotify extension is belaran@999: enabled. belaran@999: belaran@999: When you're using the inotify extension, you should notice belaran@999: no difference at all in Mercurial's belaran@999: behavior, with the sole exception of status-related commands belaran@999: running a whole lot faster than they used to. You should belaran@999: specifically expect that commands will not print different belaran@999: output; neither should they give different results. If either of belaran@999: these situations occurs, please report a bug. belaran@999: belaran@999: belaran@999: belaran@999: Flexible diff support with the <literal role="hg-ext" moreinfo="none">extdiff</literal> extension belaran@999: belaran@999: Mercurial's built-in hg belaran@999: diff command outputs plaintext unified diffs. belaran@999: belaran@999: belaran@999: $ hg diff belaran@999: diff -r 801b35c37d8b myfile belaran@999: --- a/myfile Sun Aug 16 14:05:02 2009 +0000 belaran@999: +++ b/myfile Sun Aug 16 14:05:02 2009 +0000 belaran@999: @@ -1,1 +1,2 @@ belaran@999: The first line. belaran@999: +The second line. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: If you would like to use an external tool to display belaran@999: modifications, you'll want to use the extdiff extension. This will let you belaran@999: use, for example, a graphical diff tool. belaran@999: belaran@999: The extdiff extension is belaran@999: bundled with Mercurial, so it's easy to set up. In the extensions section of your belaran@999: ~/.hgrc, simply add a belaran@999: one-line entry to enable the extension. belaran@999: [extensions] belaran@999: extdiff = belaran@999: This introduces a command named extdiff, which by default uses belaran@999: your system's diff command to generate a belaran@999: unified diff in the same form as the built-in hg diff command. belaran@999: belaran@999: belaran@999: $ hg extdiff belaran@999: --- a.801b35c37d8b/myfile 2009-08-16 14:05:02.000000000 +0000 belaran@999: +++ /tmp/extdiffl1y_s9/a/myfile 2009-08-16 14:05:02.000000000 +0000 belaran@999: @@ -1 +1,2 @@ belaran@999: The first line. belaran@999: +The second line. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: The result won't be exactly the same as with the built-in belaran@999: hg diff variations, because the belaran@999: output of diff varies from one system to belaran@999: another, even when passed the same options. belaran@999: belaran@999: As the making snapshot belaran@999: lines of output above imply, the extdiff command works by belaran@999: creating two snapshots of your source tree. The first snapshot belaran@999: is of the source revision; the second, of the target revision or belaran@999: working directory. The extdiff command generates belaran@999: these snapshots in a temporary directory, passes the name of belaran@999: each directory to an external diff viewer, then deletes the belaran@999: temporary directory. For efficiency, it only snapshots the belaran@999: directories and files that have changed between the two belaran@999: revisions. belaran@999: belaran@999: Snapshot directory names have the same base name as your belaran@999: repository. If your repository path is /quux/bar/foo, then foo will be the name of each belaran@999: snapshot directory. Each snapshot directory name has its belaran@999: changeset ID appended, if appropriate. If a snapshot is of belaran@999: revision a631aca1083f, the directory will be belaran@999: named foo.a631aca1083f. belaran@999: A snapshot of the working directory won't have a changeset ID belaran@999: appended, so it would just be foo in this example. To see what belaran@999: this looks like in practice, look again at the extdiff example above. Notice belaran@999: that the diff has the snapshot directory names embedded in its belaran@999: header. belaran@999: belaran@999: The extdiff command belaran@999: accepts two important options. The option belaran@999: lets you choose a program to view differences with, instead of belaran@999: diff. With the option, belaran@999: you can change the options that extdiff passes to the program belaran@999: (by default, these options are belaran@999: -Npru, which only make sense belaran@999: if you're running diff). In other respects, belaran@999: the extdiff command belaran@999: acts similarly to the built-in hg belaran@999: diff command: you use the same option names, syntax, belaran@999: and arguments to specify the revisions you want, the files you belaran@999: want, and so on. belaran@999: belaran@999: As an example, here's how to run the normal system belaran@999: diff command, getting it to generate context belaran@999: diffs (using the option) belaran@999: instead of unified diffs, and five lines of context instead of belaran@999: the default three (passing 5 as the argument belaran@999: to the option). belaran@999: belaran@999: belaran@999: $ hg extdiff -o -NprcC5 belaran@999: *** a.801b35c37d8b/myfile Sun Aug 16 14:05:02 2009 belaran@999: --- /tmp/extdiffl1y_s9/a/myfile Sun Aug 16 14:05:02 2009 belaran@999: *************** belaran@999: *** 1 **** belaran@999: --- 1,2 ---- belaran@999: The first line. belaran@999: + The second line. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Launching a visual diff tool is just as easy. Here's how to belaran@999: launch the kdiff3 viewer. belaran@999: hg extdiff -p kdiff3 -o belaran@999: belaran@999: If your diff viewing command can't deal with directories, belaran@999: you can easily work around this with a little scripting. For an belaran@999: example of such scripting in action with the mq extension and the belaran@999: interdiff command, see . belaran@999: belaran@999: belaran@999: Defining command aliases belaran@999: belaran@999: It can be cumbersome to remember the options to both the belaran@999: extdiff command and belaran@999: the diff viewer you want to use, so the extdiff extension lets you define belaran@999: new commands that will invoke your diff belaran@999: viewer with exactly the right options. belaran@999: belaran@999: All you need to do is edit your ~/.hgrc, and add a section named belaran@999: extdiff. Inside this belaran@999: section, you can define multiple commands. Here's how to add belaran@999: a kdiff3 command. Once you've defined belaran@999: this, you can type hg kdiff3 belaran@999: and the extdiff extension belaran@999: will run kdiff3 for you. belaran@999: [extdiff] belaran@999: cmd.kdiff3 = belaran@999: If you leave the right hand side of the definition empty, belaran@999: as above, the extdiff belaran@999: extension uses the name of the command you defined as the name belaran@999: of the external program to run. But these names don't have to belaran@999: be the same. Here, we define a command named belaran@999: hg wibble, which runs belaran@999: kdiff3. belaran@999: [extdiff] belaran@999: cmd.wibble = kdiff3 belaran@999: belaran@999: You can also specify the default options that you want to belaran@999: invoke your diff viewing program with. The prefix to use is belaran@999: opts., followed by the name belaran@999: of the command to which the options apply. This example belaran@999: defines a hg vimdiff command belaran@999: that runs the vim editor's belaran@999: DirDiff extension. belaran@999: [extdiff] belaran@999: cmd.vimdiff = vim belaran@999: opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Cherrypicking changes with the <literal role="hg-ext" moreinfo="none">transplant</literal> extension belaran@999: belaran@999: Need to have a long chat with Brendan about this. belaran@999: belaran@999: belaran@999: belaran@999: Send changes via email with the <literal role="hg-ext" moreinfo="none">patchbomb</literal> extension belaran@999: belaran@999: Many projects have a culture of change belaran@999: review, in which people send their modifications to a belaran@999: mailing list for others to read and comment on before they belaran@999: commit the final version to a shared repository. Some projects belaran@999: have people who act as gatekeepers; they apply changes from belaran@999: other people to a repository to which those others don't have belaran@999: access. belaran@999: belaran@999: Mercurial makes it easy to send changes over email for belaran@999: review or application, via its patchbomb extension. The extension is belaran@999: so named because changes are formatted as patches, and it's usual belaran@999: to send one changeset per email message. Sending a long series belaran@999: of changes by email is thus much like bombing the belaran@999: recipient's inbox, hence patchbomb. belaran@999: belaran@999: As usual, the basic configuration of the patchbomb extension takes just one or belaran@999: two lines in your belaran@999: /.hgrc. belaran@999: [extensions] belaran@999: patchbomb = belaran@999: Once you've enabled the extension, you will have a new belaran@999: command available, named email. belaran@999: belaran@999: The safest and best way to invoke the email command is to belaran@999: always run it first with the option. belaran@999: This will show you what the command would belaran@999: send, without actually sending anything. Once you've had a belaran@999: quick glance over the changes and verified that you are sending belaran@999: the right ones, you can rerun the same command, with the option belaran@999: removed. belaran@999: belaran@999: The email command belaran@999: accepts the same kind of revision syntax as every other belaran@999: Mercurial command. For example, this command will send every belaran@999: revision between 7 and tip, inclusive. belaran@999: hg email -n 7:tip belaran@999: You can also specify a repository to belaran@999: compare with. If you provide a repository but no revisions, the belaran@999: email command will belaran@999: send all revisions in the local repository that are not present belaran@999: in the remote repository. If you additionally specify revisions belaran@999: or a branch name (the latter using the option), belaran@999: this will constrain the revisions sent. belaran@999: belaran@999: It's perfectly safe to run the email command without the belaran@999: names of the people you want to send to: if you do this, it will belaran@999: just prompt you for those values interactively. (If you're belaran@999: using a Linux or Unix-like system, you should have enhanced belaran@999: readline-style editing capabilities when belaran@999: entering those headers, too, which is useful.) belaran@999: belaran@999: When you are sending just one revision, the email command will by belaran@999: default use the first line of the changeset description as the belaran@999: subject of the single email message it sends. belaran@999: belaran@999: If you send multiple revisions, the email command will usually belaran@999: send one message per changeset. It will preface the series with belaran@999: an introductory message, in which you should describe the belaran@999: purpose of the series of changes you're sending. belaran@999: belaran@999: belaran@999: Changing the behavior of patchbombs belaran@999: belaran@999: Not every project has exactly the same conventions for belaran@999: sending changes in email; the patchbomb extension tries to belaran@999: accommodate a number of variations through command line belaran@999: options. belaran@999: belaran@999: You can write a subject for the introductory belaran@999: message on the command line using the belaran@999: option. This takes one argument, the text of the subject belaran@999: to use. belaran@999: belaran@999: To change the email address from which the belaran@999: messages originate, use the belaran@999: option. This takes one argument, the email address to belaran@999: use. belaran@999: belaran@999: The default behavior is to send unified diffs belaran@999: (see for a belaran@999: description of the belaran@999: format), one per message. You can send a binary bundle belaran@999: instead with the belaran@999: option. belaran@999: belaran@999: Unified diffs are normally prefaced with a belaran@999: metadata header. You can omit this, and send unadorned belaran@999: diffs, with the option. belaran@999: belaran@999: Diffs are normally sent inline, belaran@999: in the same body part as the description of a patch. This belaran@999: makes it easiest for the largest number of readers to belaran@999: quote and respond to parts of a diff, as some mail clients belaran@999: will only quote the first MIME body part in a message. If belaran@999: you'd prefer to send the description and the diff in belaran@999: separate body parts, use the belaran@999: option. belaran@999: belaran@999: Instead of sending mail messages, you can belaran@999: write them to an mbox-format mail belaran@999: folder using the belaran@999: option. That option takes one argument, the name of the belaran@999: file to write to. belaran@999: belaran@999: If you would like to add a belaran@999: diffstat-format summary to each patch, belaran@999: and one to the introductory message, use the belaran@999: option. The diffstat command displays belaran@999: a table containing the name of each file patched, the belaran@999: number of lines affected, and a histogram showing how much belaran@999: each file is modified. This gives readers a qualitative belaran@999: glance at how complex a patch is. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Migrer vers Mercurial belaran@999: belaran@999: Une manière courante de s'essayer à un nouveau belaran@999: gestionnaire de révisions est d'expérimenter en migrant un belaran@999: projet existant, plutôt que le faire avec un nouveau projet. belaran@999: belaran@999: belaran@999: Dans cette annexe, nous discuterons comment importer belaran@999: l'historique d'un projet dans Mercurial, et à quoi faire attention belaran@999: si vous êtes habitués à un autre outil de gestion de révisions. belaran@999: belaran@999: belaran@999: belaran@999: Importer l'historique depuis un autre système belaran@999: belaran@999: Mercurial est livré avec une extension nommée belaran@999: convert, qui permet d'importer un historique belaran@999: depuis les gestionnaire de révisions les plus courants. Au moment de belaran@999: l'écriture de ce livre, il pouvait importer l'historique depuis: belaran@999: belaran@999: belaran@999: belaran@999: Subversion belaran@999: belaran@999: belaran@999: CVS belaran@999: belaran@999: belaran@999: git belaran@999: belaran@999: belaran@999: Darcs belaran@999: belaran@999: belaran@999: Bazaar belaran@999: belaran@999: belaran@999: Monotone belaran@999: belaran@999: belaran@999: GNU Arch belaran@999: belaran@999: belaran@999: Mercurial belaran@999: belaran@999: belaran@999: belaran@999: (Pour savoir pourquoi Mercurial lui même est supporté belaran@999: comme source, voir .) belaran@999: belaran@999: Vous pouvez activer l'extension de la manière belaran@999: habituelle, en éditant votre fichier ~/.hgrc belaran@999: belaran@999: [extensions] belaran@999: convert = belaran@999: belaran@999: Ceci rendra la commande hg convert belaran@999: disponible. La commande est facile à utiliser. Par exemple, la belaran@999: commande suivante va importer l'historique Subversion du framework de test Nose Unit dans Mercurial. belaran@999: belaran@999: belaran@999: $ hg convert http://python-nose.googlecode.com/svn/trunk belaran@999: belaran@999: L'extension convert opère de belaran@999: manière incrémentale. En d'autres mots, après une première exécution de belaran@999: la commande hg convert, les exécutions ultérieures belaran@999: importeront les révisions ultérieures à l'exécution précédente. belaran@999: La conversion incrémentale ne réussira que si belaran@999: vous exécutez hg convert dans le même dépôt que vous belaran@999: aviez utilisé à l'origine, ceci parce que l'extension convert belaran@999: sauvegarde un certain nombre de méta-données privées dans le fichier belaran@999: .hg/shamap (non versioné) au sein du dépôt cible. belaran@999: belaran@999: belaran@999: Lorsque vous voulez faire des modifications en utilisant belaran@999: Mercurial, le mieux est de faire un clone de l'ensemble de l'arborescence belaran@999: que vous souhaitez convertir, et de laisser l'arborescence d'origine pour belaran@999: de futures conversions incrémentales. C'est la manière la plus sûre pour vous laisser belaran@999: récupérer et fusionner les modifications futures depuis l'outil de gestion belaran@999: de révisions dans votre nouveau dépôt Mercurial. belaran@999: belaran@999: belaran@999: Convertir plusieurs branches belaran@999: belaran@999: La commande hg convert citée belaran@999: ci-dessus convertit seulement l'historique de la branche belaran@999: principale (trunk) du dépôt Subversion. Si nous utilisons belaran@999: à la place l'URL http://python-nose.googlecode.com/svn, belaran@999: Mercurial va automatiquement détecter la belaran@999: branche principale (trunk), les étiquettes belaran@999: (tags), et les branches que les dépôts belaran@999: Subversion utilisent généralement, et les importera chacun dans belaran@999: une branche Mercurial distincte. belaran@999: belaran@999: Par défaut, chaque branche Subversion importée belaran@999: dans Mercurial se voit attribuer un nom de branche. Une fois la belaran@999: conversion achevée, vous pouvez obtenir la liste des noms des branches belaran@999: actives dans le dépôt Mercurial en utilisant la commande belaran@999: hg branches -a. Si vous préférez importer les belaran@999: branches Subversion sans noms, ajoutez l'option à la commande belaran@999: hg convert. belaran@999: belaran@999: Une fois votre arborescence convertie, belaran@999: si vous souhaitez travailler selon la pratique habituelle sous Mercurial belaran@999: avec une arborescence qui ne contient qu'une seule branche, vous pouvez cloner belaran@999: cette seule branche en utilisant belaran@999: hg clone -r nomdemabranche. belaran@999: belaran@999: belaran@999: belaran@999: Associer les noms d'utilisateurs belaran@999: belaran@999: Certains outils de gestion de révisions belaran@999: ne sauvegardent, avec les modifications, que les noms belaran@999: d'utilisateurs raccourcis. Ceux-ci peuvent être difficiles à belaran@999: interpréter. La norme avec Mercurial est de sauvegarder le belaran@999: nom du committeur et son adresse belaran@999: mail, ce qui est beaucoup plus utile pour discuter avec lui belaran@999: par la suite. belaran@999: belaran@999: Si vous convertissez une arborescence depuis belaran@999: un gestionnaire de révisions qui utilise seulement les noms belaran@999: raccourcis, vous pouvez associer ces noms à des équivalents belaran@999: plus détaillés en passant l'option belaran@999: à la commande hg convert. Cette option belaran@999: attend un fichier qui contient des entrées sous la forme suivante: belaran@999: belaran@999: belaran@999: arist = Aristotle <aristotle@phil.example.gr> belaran@999: soc = Socrates <socrates@phil.example.gr> belaran@999: belaran@999: Quand convert trouve une belaran@999: modification associée au nom arist dans le belaran@999: dépôt de source, il va utiliser le nom Aristotle belaran@999: <aristotle@phil.example.gr> dans les révisions belaran@999: Mercurial. Si aucune correspondance n'est trouvé, il utilise belaran@999: le nom tel quel. belaran@999: belaran@999: belaran@999: belaran@999: Nettoyer l'arboresence belaran@999: belaran@999: Tous les projets n'ont pas un historique parfait. belaran@999: Il peut y avoir des répertoires qui n'auraient jamais dû être ajoutés, belaran@999: un fichier qui est trop volumineux, ou même une partie de la belaran@999: hiérarchie qui devrait être réorganisée. belaran@999: belaran@999: L'extension convert permet belaran@999: d'utiliser un fichier d'association qui peut belaran@999: réorganiser les fichiers et les répertoires dans un projet lors de belaran@999: l'importation de son historique. Ceci est utile non seulement quand vous belaran@999: importez l'historique d'un autre gestionnaire de révisions, mais belaran@999: aussi pour nettoyer ou réorganiser l'arborescence d'un projet belaran@999: Mercurial. belaran@999: belaran@999: Pour indiquer le fichier d'association, on utilise belaran@999: l'option en lui fournissant un nom de belaran@999: fichier. Le fichier d'association contient des lignes de la forme belaran@999: suivante : belaran@999: belaran@999: # Ceci est un commentaire. belaran@999: # Les lignes vides sont ignorées. belaran@999: belaran@999: include path/to/file belaran@999: belaran@999: exclude path/to/file belaran@999: belaran@999: rename from/some/path to/some/other/place belaran@999: belaran@999: belaran@999: La directive include inclut un belaran@999: fichier, ou l'ensemble des fichiers d'un répertoire, dans le dépôt belaran@999: de destination. La directive exclude omet les belaran@999: fichiers ou répertoires du dépôt. Ceci inclut aussi les autres belaran@999: fichiers et répertoires qui ne sont pas explicitement inclus. belaran@999: La directive exclude entraine l'omission belaran@999: des fichiers ou répertoires, et autres fichiers qui ne sont pas belaran@999: explicitement inclus. belaran@999: belaran@999: Pour déplacer un fichier ou un répertoire d'un belaran@999: emplacement à un autre, utilisez la directive belaran@999: rename. Si vous avez besoin de déplacer un belaran@999: fichier ou un répertoire depuis un sous répertoire dans la racine belaran@999: du dépôt, utilisez . comme second argument de belaran@999: la directive rename. belaran@999: belaran@999: belaran@999: belaran@999: Améliorer les performances de la conversion Subversion belaran@999: belaran@999: Vous aurez souvent besoin de plusieurs essais belaran@999: avant d'arriver à la parfaite combinaison de fichier d'association de fichiers, belaran@999: de fichier d'association de noms d'utilisateurs et des autres paramètres. Or, belaran@999: convertir un dépôt Mercurial via un protocole comme ssh belaran@999: ou http peut être des milliers de fois plus long belaran@999: que ce dont le système d'exploitation est en fait capable de faire, belaran@999: à cause des latence réseau. Ceci peut rendre la conception de cette belaran@999: combinaison parfaite très douloureuse. belaran@999: belaran@999: La commande svnsync belaran@999: peut grandement améliorer la vitesse de conversion d'un dépôt belaran@999: Subversion. Il s'agit d'un programme de miroir de dépôt Subversion belaran@999: en lecture seule. L'idée est de créer un miroir local d'une belaran@999: arborescence Subversion, puis de convertir ce miroir en dépôt belaran@999: Mercurial. belaran@999: belaran@999: Supposez que nous voulions convertir le dépôt belaran@999: Subversion du populaire projet Memcached en une arborescence Mercurial. belaran@999: Tout d'abord, nous créons un dépôt Subversion local. belaran@999: belaran@999: $ svnadmin create memcached-mirror belaran@999: belaran@999: Puis, nous allons mettre en place un hook Subversion belaran@999: dont svnsync a besoin. belaran@999: belaran@999: $ echo '#!/bin/sh' > memcached-mirror/hooks/pre-revprop-change belaran@999: $ chmod +x memcached-mirror/hooks/pre-revprop-change belaran@999: belaran@999: Nous initialisons ensuite svnsync dans ce belaran@999: dépôt. belaran@999: belaran@999: $ svnsync --init file://`pwd`/memcached-mirror \ belaran@999: http://code.sixapart.com/svn/memcached belaran@999: belaran@999: La prochaine étape est de commencer le processus de belaran@999: mirroring de svnsync. belaran@999: belaran@999: $ svnsync sync file://`pwd`/memcached-mirror belaran@999: belaran@999: Enfin, nous importons l'historique de notre dépôt belaran@999: local Subversion dans Mercurial. belaran@999: belaran@999: $ hg convert memcached-mirror belaran@999: belaran@999: Nous pouvons utiliser ce processus de manière belaran@999: incrémentale, si le dépôt Subversion est toujours en activité. belaran@999: Il suffit d'exécuter de nouveau svnsync pour belaran@999: récupérer les récentes modifications dans notre miroir, puis hg belaran@999: convert belaran@999: les importe dans notre arborescence Mercurial. belaran@999: belaran@999: Il y a deux avantages à utiliser un import à deux belaran@999: étages comme avec svnsync. Le premier belaran@999: est qu'il utilise du code de synchronisation réseau de Subversion belaran@999: plus efficace que la commande hg convert, belaran@999: et donc transfère moins de données par le réseau. Le deuxième belaran@999: est que l'import depuis un dépôt Subversion local est si rapide que belaran@999: vous pouvez peaufiner et réitérer les paramètres de conversion de belaran@999: ce dernier sans souffrir de la qualité de la connexion réseau. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Migrer depuis Subversion belaran@999: belaran@999: Subversion est le système de gestion de versions belaran@999: open source le plus populaire aujourd'hui. Bien qu'il y ait des belaran@999: différences entre Mercurial et Subversion, faire la transition de belaran@999: l'un à l'autre n'est pas très difficile. Les deux disposent en effet belaran@999: de jeux de commandes similaires et d'interfaces similaires. belaran@999: belaran@999: belaran@999: Différences philosophiques belaran@999: belaran@999: La différence fondamentale entre Subversion et belaran@999: Mercurial est bien évidement que Subversion est centralisé, alors belaran@999: que Mercurial est distribué. Puisque que Mercurial enregistre tout belaran@999: l'historique d'un projet sur votre disque dur local, il n'a besoin belaran@999: d'effectuer des accès au réseau que lorsque vous voulez belaran@999: explicitement communiquer avec un autre dépôt. Subversion, par contre, belaran@999: ne conserve que peu d'informations localement, et le client belaran@999: doit donc communiquer avec le serveur central pour la belaran@999: plupart des opérations communes. belaran@999: belaran@999: Subversion s'en tire plus ou moins bien sans notion belaran@999: de branche réellement bien définie : quelle portion de l'espace de nommage belaran@999: du serveur est une branche est une simple question de convention, le belaran@999: logiciel n'imposant rien à ce sujet. Mercurial considère belaran@999: un dépôt comme un élément de la gestion des branches. belaran@999: belaran@999: belaran@999: Portée des commandes belaran@999: belaran@999: Puisque que Subversion ne sait pas réellement belaran@999: quelle partie de son espace de nommage est en fait une branche, il belaran@999: traite la plupart des commandes comme des requêtes à exécuter sur le belaran@999: répertoire où vous vous situez, et ses sous répertoires. Par exemple, belaran@999: si vous exécutez svn log, vous verrez l'historique belaran@999: de la partie de l'arborescence où vous vous situez, et non de la belaran@999: hiérarchie entière. belaran@999: belaran@999: Les commandes de Mercurial ont un comportement belaran@999: différent : toutes les commandes s'appliquent à l'ensemble de l'arborescence belaran@999: du dépôt. Exécutez la commande hg log et elle vous belaran@999: donnera l'historique de l'ensemble de l'arborescence, quel que soit le belaran@999: sous-répertoire où vous vous situez. Si belaran@999: vous souhaitez obtenir l'historique d'un répertoire ou seulement d'un belaran@999: fichier, ajouter simplement le nom de celui-ci à la commande, par belaran@999: exemple hg log src. belaran@999: belaran@999: De ma propre expérience, cette différence dans leur belaran@999: comportement par défaut est probablement ce qui risque de vous belaran@999: surprendre le plus si vous passez régulièrement d'un outil à l'autre. belaran@999: belaran@999: belaran@999: belaran@999: Opération multi utilisateur et sécurité belaran@999: belaran@999: Avec Subversion, il est normal (bien que légèrement belaran@999: désapprouvé) que différentes personnes collaborent sur une seule belaran@999: branche. Si Alice et Bob travaillent ensemble, et Alice ajoute ses belaran@999: modifications à leur branche partagée, Bob doit alors mettre à jour belaran@999: sa vue de la branche avant de pouvoir appliquer un commit. belaran@999: Puisqu'il n'a, à ce moment, pas effectué de commit belaran@999: des modifications qu'il a faites, il se peut qu'il ne corrompe belaran@999: ou ne perde belaran@999: ses modifications pendant ou après la mise à jour. belaran@999: belaran@999: Mercurial encourage, à l'inverse, un modèle belaran@999: "commit-puis-merge". Avant de récupérer des modifications depuis le belaran@999: serveur, ou avant d'y envoyer les siennes, Bob enregistre ses belaran@999: modifications de manière locale en appliquant un commit. C'est à dire belaran@999: que si Alice avait envoyé ses modifications sur le serveur avant belaran@999: que Bob n'envoie les siennes, ce dernier ne pourra le faire belaran@999: qu'après avoir récupéré et fusionné celles d'Alice avec les siennes. belaran@999: Si Bob fait alors une belaran@999: erreur lors de la fusion, il pourra toujours restaurer sa version, pour belaran@999: laquelle il avait appliqué le commit. belaran@999: belaran@999: Il est important de souligner qu'il s'agit de la belaran@999: manière habituelle de travailler avec ces outils. Subversion propose belaran@999: une manière plus sûre de "travailler-dans-votre-propre-branche", mais elle belaran@999: est assez complexe pour que, en pratique, elle ne soit que rarement utilisé. belaran@999: Mercurial propose de son côté un mode un peu moins sûr, permettant de belaran@999: récupérer des modifications par dessus des modifications non belaran@999: committées, qui reste toutefois très peu répandu. belaran@999: belaran@999: belaran@999: belaran@999: Publication vs changement locaux belaran@999: belaran@999: Une commande Subversion svn belaran@999: commit publie immédiatement les modifications sur le belaran@999: serveur, où elles peuvent être vu par n'importe qui doté d'un privilège belaran@999: de lecture. belaran@999: belaran@999: Avec Mercurial, les modifications sont toujours d'abord belaran@999: enregistrées localement, et doivent être par la suite transférés par belaran@999: la commande hg push. belaran@999: belaran@999: Chaque approche a ses avantages et ses inconvénients. belaran@999: Le modèle Subversion implique que les modifications soient publiées, et belaran@999: donc disponibles immédiatement. D'un autre coté, cela implique aussi belaran@999: que, pour pouvoir utiliser le logiciel normalement, un utilisateur doit belaran@999: avoir les droits d'écriture dans le dépôt, et ce privilège n'est pas concédé belaran@999: facilement par la plupart des projets Open Source. belaran@999: belaran@999: L'approche de Mercurial permet à quiconque de faire belaran@999: un clone du dépôt et d'y ajouter ses modifications sans jamais avoir belaran@999: besoin de la permission de quiconque, et l'on peut même publier ses belaran@999: modifications et continuer à participer comme on le désire. Toutefois, la belaran@999: distinction entre les commits et le transfert de ces derniers présente belaran@999: le risque que quelqu'un applique ses modifications par un commit local belaran@999: sur son portable et parte se promener pendant quelques jours en ayant belaran@999: oublié de les transférer, ce qui peut, dans certains rares cas, belaran@999: bloquer temporairement ses collaborateurs. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Références des commandes belaran@999: belaran@999: belaran@999: Commandes Subversion et leurs équivalents Mercurial belaran@999: belaran@999: belaran@999: belaran@999: Subversion belaran@999: Mercurial belaran@999: Notes belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: svn add belaran@999: hg add belaran@999: belaran@999: belaran@999: belaran@999: svn blame belaran@999: hg annotate belaran@999: belaran@999: belaran@999: belaran@999: svn cat belaran@999: hg cat belaran@999: belaran@999: belaran@999: belaran@999: svn checkout belaran@999: hg clone belaran@999: belaran@999: belaran@999: belaran@999: svn cleanup belaran@999: n/a belaran@999: Aucun nettoyage nécessaire. belaran@999: belaran@999: belaran@999: svn commit belaran@999: hg commit; hg belaran@999: push belaran@999: hg push publie les modifications belaran@999: après un commit. belaran@999: belaran@999: belaran@999: svn copy belaran@999: hg clone belaran@999: Pour créer une nouvelle branche belaran@999: belaran@999: belaran@999: svn copy belaran@999: hg copy belaran@999: Pour copier des fichiers ou des répertoires belaran@999: belaran@999: belaran@999: svn delete (svn belaran@999: remove) belaran@999: hg remove belaran@999: belaran@999: belaran@999: belaran@999: svn diff belaran@999: hg diff belaran@999: belaran@999: belaran@999: belaran@999: svn export belaran@999: hg archive belaran@999: belaran@999: belaran@999: belaran@999: svn help belaran@999: hg help belaran@999: belaran@999: belaran@999: belaran@999: svn import belaran@999: hg addremove; hg belaran@999: commit belaran@999: belaran@999: belaran@999: belaran@999: svn info belaran@999: hg parents belaran@999: Affiche la version sur la base de laquelle on travaille belaran@999: belaran@999: belaran@999: svn info belaran@999: hg showconfig belaran@999: paths.default belaran@999: Affiche de quelle URL est extrait ce dépôt belaran@999: belaran@999: belaran@999: svn list belaran@999: hg manifest belaran@999: belaran@999: belaran@999: belaran@999: svn log belaran@999: hg log belaran@999: belaran@999: belaran@999: belaran@999: svn merge belaran@999: hg merge belaran@999: belaran@999: belaran@999: belaran@999: svn mkdir belaran@999: n/a belaran@999: Mercurial ne versionne pas les répertoires belaran@999: belaran@999: belaran@999: svn move (svn belaran@999: rename) belaran@999: hg rename belaran@999: belaran@999: belaran@999: belaran@999: svn resolved belaran@999: hg resolve -m belaran@999: belaran@999: belaran@999: belaran@999: svn revert belaran@999: hg revert belaran@999: belaran@999: belaran@999: belaran@999: svn status belaran@999: hg status belaran@999: belaran@999: belaran@999: belaran@999: svn update belaran@999: hg pull -u belaran@999: belaran@999: belaran@999: belaran@999: belaran@999:
belaran@999:
belaran@999:
belaran@999: belaran@999: belaran@999: Conseils utiles pour les débutants belaran@999: belaran@999: Avec la plupart des gestionnaire de versions, afficher belaran@999: un diff associé à une révision peut être assez douloureux. Par exemple, belaran@999: avec Subversion, pour voir ce qui a été modifiée dans la révision 104654, belaran@999: vous devez saisir svn diff -r104653:104654. Mercurial belaran@999: élimine le besoin de saisir l'identifiant d'une révision deux fois dans belaran@999: ce cas classique. Pour un simple diff, hg belaran@999: export 104654 suffit. Pour obtenir une entrée du journal suivie d'un diff, belaran@999: hg log -r104654 -p. belaran@999: belaran@999: Quand vous exécutez la commande hg status belaran@999: sans aucun argument, elle affiche l'état de l'ensemble de l'arborescence, belaran@999: avec des chemins relatifs partant de la racine du dépôt. Ceci rend belaran@999: difficile de copier un nom de fichier depuis la sortie de la commande belaran@999: hg status dans une autre ligne de commande. Si vous belaran@999: fournissez un fichier ou un répertoire à la commande hg belaran@999: status, elle va afficher les chemins relatif depuis votre belaran@999: répertoire courant à la place. Ainsi, pour avoir un état sur l'ensemble belaran@999: de l'arborescence à l'aide de hg status, avec des belaran@999: chemins relatifs à votre répertoire courant, et non la racine du dépôt, belaran@999: ajoutez la sortie de hg root à la commande belaran@999: hg status. Vous pouvez le faire aisément sur un belaran@999: système Unix ainsi : belaran@999: belaran@999: $ hg status `hg root` belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Mercurial Queues reference belaran@999: belaran@999: belaran@999: MQ command reference belaran@999: belaran@999: For an overview of the commands provided by MQ, use the belaran@999: command hg help mq. belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qapplied</command>—print belaran@999: applied patches belaran@999: belaran@999: The qapplied command belaran@999: prints the current stack of applied patches. Patches are belaran@999: printed in oldest-to-newest order, so the last patch in the belaran@999: list is the top patch. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qcommit</command>—commit belaran@999: changes in the queue repository belaran@999: belaran@999: The qcommit command belaran@999: commits any outstanding changes in the .hg/patches belaran@999: repository. This command only works if the .hg/patches belaran@999: directory is a repository, i.e. you created the directory belaran@999: using hg qinit or belaran@999: ran hg init in the directory belaran@999: after running qinit. belaran@999: belaran@999: This command is shorthand for hg belaran@999: commit --cwd .hg/patches. belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qdelete</command>—delete a patch belaran@999: from the <filename role="special" moreinfo="none">series</filename> belaran@999: file belaran@999: belaran@999: The qdelete command belaran@999: removes the entry for a patch from the series file in the .hg/patches belaran@999: directory. It does not pop the patch if the patch is already belaran@999: applied. By default, it does not delete the patch file; use belaran@999: the option belaran@999: to do that. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Delete the belaran@999: patch file. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qdiff</command>—print a belaran@999: diff of the topmost applied patch belaran@999: belaran@999: The qdiff command belaran@999: prints a diff of the topmost applied patch. It is equivalent belaran@999: to hg diff -r-2:-1. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qfold</command>—move belaran@999: applied patches into repository history belaran@999: belaran@999: The hg qfinish command converts the belaran@999: specified applied patches into permanent changes by moving belaran@999: them out of MQ's control so that they will be treated as belaran@999: normal repository history. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qfold</command>—merge belaran@999: (<quote>fold</quote>) several patches into one belaran@999: belaran@999: The qfold command belaran@999: merges multiple patches into the topmost applied patch, so belaran@999: that the topmost applied patch makes the union of all of the belaran@999: changes in the patches in question. belaran@999: belaran@999: The patches to fold must not be applied; qfold will exit with an error if belaran@999: any is. The order in which patches are folded is significant; belaran@999: hg qfold a b means belaran@999: apply the current topmost patch, followed by belaran@999: a, followed by belaran@999: b. belaran@999: belaran@999: The comments from the folded patches are appended to the belaran@999: comments of the destination patch, with each block of comments belaran@999: separated by three asterisk belaran@999: (*) characters. Use the belaran@999: option to belaran@999: edit the commit message for the combined patch/changeset after belaran@999: the folding has completed. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Edit the belaran@999: commit message and patch description for the newly folded belaran@999: patch. belaran@999: belaran@999: : Use the belaran@999: contents of the given file as the new commit message and belaran@999: patch description for the folded patch. belaran@999: belaran@999: : Use the belaran@999: given text as the new commit message and patch description belaran@999: for the folded patch. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qheader</command>—display the belaran@999: header/description of a patch belaran@999: belaran@999: The qheader command belaran@999: prints the header, or description, of a patch. By default, it belaran@999: prints the header of the topmost applied patch. Given an belaran@999: argument, it prints the header of the named patch. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qimport</command>—import belaran@999: a third-party patch into the queue belaran@999: belaran@999: The qimport command belaran@999: adds an entry for an external patch to the series file, and copies the patch belaran@999: into the .hg/patches directory. It adds belaran@999: the entry immediately after the topmost applied patch, but belaran@999: does not push the patch. belaran@999: belaran@999: If the .hg/patches directory is a belaran@999: repository, qimport belaran@999: automatically does an hg add belaran@999: of the imported patch. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qinit</command>—prepare belaran@999: a repository to work with MQ belaran@999: belaran@999: The qinit command belaran@999: prepares a repository to work with MQ. It creates a directory belaran@999: called .hg/patches. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Create belaran@999: .hg/patches as a repository belaran@999: in its own right. Also creates a .hgignore file that will belaran@999: ignore the status belaran@999: file. belaran@999: belaran@999: belaran@999: When the .hg/patches directory is a belaran@999: repository, the qimport belaran@999: and qnew commands belaran@999: automatically hg add new belaran@999: patches. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qnew</command>—create a belaran@999: new patch belaran@999: belaran@999: The qnew command belaran@999: creates a new patch. It takes one mandatory argument, the belaran@999: name to use for the patch file. The newly created patch is belaran@999: created empty by default. It is added to the series file after the current belaran@999: topmost applied patch, and is immediately pushed on top of belaran@999: that patch. belaran@999: belaran@999: If qnew finds modified belaran@999: files in the working directory, it will refuse to create a new belaran@999: patch unless the option is used belaran@999: (see below). This behavior allows you to qrefresh your topmost applied belaran@999: patch before you apply a new patch on top of it. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Create a new belaran@999: patch if the contents of the working directory are belaran@999: modified. Any outstanding modifications are added to the belaran@999: newly created patch, so after this command completes, the belaran@999: working directory will no longer be modified. belaran@999: belaran@999: : Use the given belaran@999: text as the commit message. This text will be stored at belaran@999: the beginning of the patch file, before the patch belaran@999: data. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qnext</command>—print belaran@999: the name of the next patch belaran@999: belaran@999: The qnext command belaran@999: prints the name name of the next patch in the series file after the topmost belaran@999: applied patch. This patch will become the topmost applied belaran@999: patch if you run qpush. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qpop</command>—pop belaran@999: patches off the stack belaran@999: belaran@999: The qpop command belaran@999: removes applied patches from the top of the stack of applied belaran@999: patches. By default, it removes only one patch. belaran@999: belaran@999: This command removes the changesets that represent the belaran@999: popped patches from the repository, and updates the working belaran@999: directory to undo the effects of the patches. belaran@999: belaran@999: This command takes an optional argument, which it uses as belaran@999: the name or index of the patch to pop to. If given a name, it belaran@999: will pop patches until the named patch is the topmost applied belaran@999: patch. If given a number, qpop treats the number as an belaran@999: index into the entries in the series file, counting from zero belaran@999: (empty lines and lines containing only comments do not count). belaran@999: It pops patches until the patch identified by the given index belaran@999: is the topmost applied patch. belaran@999: belaran@999: The qpop command does belaran@999: not read or write patches or the series file. It is thus safe to belaran@999: qpop a patch that you have belaran@999: removed from the series belaran@999: file, or a patch that you have renamed or deleted entirely. belaran@999: In the latter two cases, use the name of the patch as it was belaran@999: when you applied it. belaran@999: belaran@999: By default, the qpop belaran@999: command will not pop any patches if the working directory has belaran@999: been modified. You can override this behavior using the belaran@999: option, belaran@999: which reverts all modifications in the working belaran@999: directory. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Pop all belaran@999: applied patches. This returns the repository to its state belaran@999: before you applied any patches. belaran@999: belaran@999: : Forcibly belaran@999: revert any modifications to the working directory when belaran@999: popping. belaran@999: belaran@999: : Pop a patch belaran@999: from the named queue. belaran@999: belaran@999: belaran@999: The qpop command belaran@999: removes one line from the end of the status file for each patch that it belaran@999: pops. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qprev</command>—print belaran@999: the name of the previous patch belaran@999: belaran@999: The qprev command belaran@999: prints the name of the patch in the series file that comes before the belaran@999: topmost applied patch. This will become the topmost applied belaran@999: patch if you run qpop. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qpush</command>—push belaran@999: patches onto the stack belaran@999: belaran@999: The qpush command adds belaran@999: patches onto the applied stack. By default, it adds only one belaran@999: patch. belaran@999: belaran@999: This command creates a new changeset to represent each belaran@999: applied patch, and updates the working directory to apply the belaran@999: effects of the patches. belaran@999: belaran@999: The default data used when creating a changeset are as belaran@999: follows: belaran@999: belaran@999: The commit date and time zone are the current belaran@999: date and time zone. Because these data are used to belaran@999: compute the identity of a changeset, this means that if belaran@999: you qpop a patch and belaran@999: qpush it again, the belaran@999: changeset that you push will have a different identity belaran@999: than the changeset you popped. belaran@999: belaran@999: The author is the same as the default used by belaran@999: the hg commit belaran@999: command. belaran@999: belaran@999: The commit message is any text from the patch belaran@999: file that comes before the first diff header. If there is belaran@999: no such text, a default commit message is used that belaran@999: identifies the name of the patch. belaran@999: belaran@999: If a patch contains a Mercurial patch header, belaran@999: the information in the patch header overrides these belaran@999: defaults. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Push all belaran@999: unapplied patches from the series file until there are belaran@999: none left to push. belaran@999: belaran@999: : Add the name belaran@999: of the patch to the end of the commit message. belaran@999: belaran@999: : If a patch belaran@999: fails to apply cleanly, use the entry for the patch in belaran@999: another saved queue to compute the parameters for a belaran@999: three-way merge, and perform a three-way merge using the belaran@999: normal Mercurial merge machinery. Use the resolution of belaran@999: the merge as the new patch content. belaran@999: belaran@999: : Use the belaran@999: named queue if merging while pushing. belaran@999: belaran@999: belaran@999: The qpush command belaran@999: reads, but does not modify, the series file. It appends one line belaran@999: to the hg status file for belaran@999: each patch that it pushes. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qrefresh</command>—update the belaran@999: topmost applied patch belaran@999: belaran@999: The qrefresh command belaran@999: updates the topmost applied patch. It modifies the patch, belaran@999: removes the old changeset that represented the patch, and belaran@999: creates a new changeset to represent the modified belaran@999: patch. belaran@999: belaran@999: The qrefresh command belaran@999: looks for the following modifications: belaran@999: belaran@999: Changes to the commit message, i.e. the text belaran@999: before the first diff header in the patch file, are belaran@999: reflected in the new changeset that represents the belaran@999: patch. belaran@999: belaran@999: Modifications to tracked files in the working belaran@999: directory are added to the patch. belaran@999: belaran@999: Changes to the files tracked using hg add, hg copy, hg remove, or hg rename. Added files and copy belaran@999: and rename destinations are added to the patch, while belaran@999: removed files and rename sources are removed. belaran@999: belaran@999: belaran@999: Even if qrefresh belaran@999: detects no changes, it still recreates the changeset that belaran@999: represents the patch. This causes the identity of the belaran@999: changeset to differ from the previous changeset that belaran@999: identified the patch. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Modify belaran@999: the commit and patch description, using the preferred text belaran@999: editor. belaran@999: belaran@999: : Modify belaran@999: the commit message and patch description, using the given belaran@999: text. belaran@999: belaran@999: : Modify belaran@999: the commit message and patch description, using text from belaran@999: the given file. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qrename</command>—rename belaran@999: a patch belaran@999: belaran@999: The qrename command belaran@999: renames a patch, and changes the entry for the patch in the belaran@999: series file. belaran@999: belaran@999: With a single argument, qrename renames the topmost belaran@999: applied patch. With two arguments, it renames its first belaran@999: argument to its second. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qseries</command>—print belaran@999: the entire patch series belaran@999: belaran@999: The qseries command belaran@999: prints the entire patch series from the series file. It prints only patch belaran@999: names, not empty lines or comments. It prints in order from belaran@999: first to be applied to last. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qtop</command>—print the belaran@999: name of the current patch belaran@999: belaran@999: The qtop prints the belaran@999: name of the topmost currently applied patch. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-ext-mq" moreinfo="none">qunapplied</command>—print patches belaran@999: not yet applied belaran@999: belaran@999: The qunapplied command belaran@999: prints the names of patches from the series file that are not yet belaran@999: applied. It prints them in order from the next patch that belaran@999: will be pushed to the last. belaran@999: belaran@999: belaran@999: belaran@999: <command role="hg-cmd" moreinfo="none">hg strip</command>—remove a belaran@999: revision and descendants belaran@999: belaran@999: The hg strip command belaran@999: removes a revision, and all of its descendants, from the belaran@999: repository. It undoes the effects of the removed revisions belaran@999: from the repository, and updates the working directory to the belaran@999: first parent of the removed revision. belaran@999: belaran@999: The hg strip command belaran@999: saves a backup of the removed changesets in a bundle, so that belaran@999: they can be reapplied if removed in error. belaran@999: belaran@999: Options: belaran@999: belaran@999: : Save belaran@999: unrelated changesets that are intermixed with the stripped belaran@999: changesets in the backup bundle. belaran@999: belaran@999: : If a belaran@999: branch has multiple heads, remove all heads. belaran@999: belaran@999: : Do belaran@999: not save a backup bundle. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: MQ file reference belaran@999: belaran@999: belaran@999: The <filename role="special" moreinfo="none">series</filename> belaran@999: file belaran@999: belaran@999: The series file belaran@999: contains a list of the names of all patches that MQ can apply. belaran@999: It is represented as a list of names, with one name saved per belaran@999: line. Leading and trailing white space in each line are belaran@999: ignored. belaran@999: belaran@999: Lines may contain comments. A comment begins with the belaran@999: # character, and extends to belaran@999: the end of the line. Empty lines, and lines that contain only belaran@999: comments, are ignored. belaran@999: belaran@999: You will often need to edit the series file by hand, hence the belaran@999: support for comments and empty lines noted above. For belaran@999: example, you can comment out a patch temporarily, and qpush will skip over that patch belaran@999: when applying patches. You can also change the order in which belaran@999: patches are applied by reordering their entries in the belaran@999: series file. belaran@999: belaran@999: Placing the series belaran@999: file under revision control is also supported; it is a good belaran@999: idea to place all of the patches that it refers to under belaran@999: revision control, as well. If you create a patch directory belaran@999: using the belaran@999: option to qinit, this will belaran@999: be done for you automatically. belaran@999: belaran@999: belaran@999: belaran@999: The <filename role="special" moreinfo="none">status</filename> belaran@999: file belaran@999: belaran@999: The status file belaran@999: contains the names and changeset hashes of all patches that MQ belaran@999: currently has applied. Unlike the series file, this file is not belaran@999: intended for editing. You should not place this file under belaran@999: revision control, or modify it in any way. It is used by MQ belaran@999: strictly for internal book-keeping. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Installer Mercurial à partir des sources belaran@999: belaran@999: belaran@999: Pour un système Unix ou similaire belaran@999: belaran@999: Si vous utilisez un système Unix ou similaire, pour lequel belaran@999: une version récente de Python (2.3 ou plus) est disponible, l'installation belaran@999: de Mercurial à partir des sources est simple. belaran@999: belaran@999: Téléchargez un paquet récent depuis http://www.selenic.com/mercurial/download. belaran@999: belaran@999: Extrayez le paquet : belaran@999: gzip -dc mercurial-MYVERSION.tar.gz | tar xf - belaran@999: belaran@999: Allez dans le répertoires où les sources ont belaran@999: été extraites et exécutez le script d'installation. Ce dernier compilera belaran@999: Mercurial et l'installera dans votre répertoire utilisateur. belaran@999: cd mercurial-MYVERSION belaran@999: python setup.py install --force --home=$HOME belaran@999: belaran@999: belaran@999: Lorsque l'installation est terminée, Mercurial se belaran@999: trouvera dans le répertoire bin de votre répertoire belaran@999: utilisateur. belaran@999: N'oubliez pas de vérifier que ce répertoire se trouve dans la liste belaran@999: des répertoires où votre shell recherche les exécutables. belaran@999: belaran@999: Vous devrez vraisemblablement définir la variable belaran@999: d'environnement PYTHONPATH de manière à ce que belaran@999: l'exécutable de Mercurial puisse trouver le reste des paquets logiciels. belaran@999: Par exemple, sur mon ordinateur portable, je dois le définir ainsi: belaran@999: /home/bos/lib/python. Le chemin exact à utiliser belaran@999: dépendra de la manière dont Python aura été construit pour votre belaran@999: système. Il ne devrait pas être difficile de le trouver. En cas de belaran@999: doute, lisez le texte généré lors de l'installation ci-dessus, et belaran@999: recherchez l'emplacement où le contenu du répertoire belaran@999: mercurial a été installé. belaran@999: belaran@999: belaran@999: belaran@999: Pour Windows belaran@999: belaran@999: Construire et installer Mercurial sous Windows nécessite belaran@999: des outils logiciels divers, une certaine connaissance technique et une belaran@999: bonne dose de patience. Je vous déconseille fortement belaran@999: de tenter de le faire si vous êtes un simple utilisateur. belaran@999: A moins que vous n'ayez l'intention de "hacker" Mercurial, je vous belaran@999: suggère d'avoir recours à un paquet d'installation de la version binaire. belaran@999: belaran@999: Si vous avez vraiment l'intention de construire belaran@999: Mercurial à partir des sources sous Windows, suivez les indications pour belaran@999: ce chemin laborieux sur le wiki de Mercurial : http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall, belaran@999: et préparez vous à un travail épineux. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Open Publication License belaran@999: belaran@999: Version 1.0, 8 June 1999 belaran@999: belaran@999: belaran@999: Requirements on both unmodified and modified belaran@999: versions belaran@999: belaran@999: The Open Publication works may be reproduced and distributed belaran@999: in whole or in part, in any medium physical or electronic, belaran@999: provided that the terms of this license are adhered to, and that belaran@999: this license or an incorporation of it by reference (with any belaran@999: options elected by the author(s) and/or publisher) is displayed belaran@999: in the reproduction. belaran@999: belaran@999: Proper form for an incorporation by reference is as belaran@999: follows: belaran@999: belaran@999:
belaran@999: Copyright (c) year by belaran@999: author's name or designee. This material belaran@999: may be distributed only subject to the terms and conditions belaran@999: set forth in the Open Publication License, belaran@999: vx.y or later (the latest version is belaran@999: presently available at http://www.opencontent.org/openpub/). belaran@999:
belaran@999: belaran@999: The reference must be immediately followed with any options belaran@999: elected by the author(s) and/or publisher of the document (see belaran@999: ). belaran@999: belaran@999: Commercial redistribution of Open Publication-licensed belaran@999: material is permitted. belaran@999: belaran@999: Any publication in standard (paper) book form shall require belaran@999: the citation of the original publisher and author. The publisher belaran@999: and author's names shall appear on all outer surfaces of the belaran@999: book. On all outer surfaces of the book the original publisher's belaran@999: name shall be as large as the title of the work and cited as belaran@999: possessive with respect to the title. belaran@999: belaran@999:
belaran@999: belaran@999: Copyright belaran@999: belaran@999: The copyright to each Open Publication is owned by its belaran@999: author(s) or designee. belaran@999: belaran@999: belaran@999: belaran@999: Scope of license belaran@999: belaran@999: The following license terms apply to all Open Publication belaran@999: works, unless otherwise explicitly stated in the belaran@999: document. belaran@999: belaran@999: Mere aggregation of Open Publication works or a portion of belaran@999: an Open Publication work with other works or programs on the belaran@999: same media shall not cause this license to apply to those other belaran@999: works. The aggregate work shall contain a notice specifying the belaran@999: inclusion of the Open Publication material and appropriate belaran@999: copyright notice. belaran@999: belaran@999: Severability. If any part belaran@999: of this license is found to be unenforceable in any belaran@999: jurisdiction, the remaining portions of the license remain in belaran@999: force. belaran@999: belaran@999: No warranty. Open belaran@999: Publication works are licensed and provided as is belaran@999: without warranty of any kind, express or implied, including, but belaran@999: not limited to, the implied warranties of merchantability and belaran@999: fitness for a particular purpose or a warranty of belaran@999: non-infringement. belaran@999: belaran@999: belaran@999: belaran@999: Requirements on modified works belaran@999: belaran@999: All modified versions of documents covered by this license, belaran@999: including translations, anthologies, compilations and partial belaran@999: documents, must meet the following requirements: belaran@999: belaran@999: belaran@999: The modified version must be labeled as belaran@999: such. belaran@999: belaran@999: The person making the modifications must be belaran@999: identified and the modifications dated. belaran@999: belaran@999: Acknowledgement of the original author and belaran@999: publisher if applicable must be retained according to normal belaran@999: academic citation practices. belaran@999: belaran@999: The location of the original unmodified document belaran@999: must be identified. belaran@999: belaran@999: The original author's (or authors') name(s) may belaran@999: not be used to assert or imply endorsement of the resulting belaran@999: document without the original author's (or authors') belaran@999: permission. belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: Good-practice recommendations belaran@999: belaran@999: In addition to the requirements of this license, it is belaran@999: requested from and strongly recommended of redistributors belaran@999: that: belaran@999: belaran@999: belaran@999: If you are distributing Open Publication works belaran@999: on hardcopy or CD-ROM, you provide email notification to the belaran@999: authors of your intent to redistribute at least thirty days belaran@999: before your manuscript or media freeze, to give the authors belaran@999: time to provide updated documents. This notification should belaran@999: describe modifications, if any, made to the document. belaran@999: belaran@999: All substantive modifications (including belaran@999: deletions) be either clearly marked up in the document or belaran@999: else described in an attachment to the document. belaran@999: belaran@999: Finally, while it is not mandatory under this belaran@999: license, it is considered good form to offer a free copy of belaran@999: any hardcopy and CD-ROM expression of an Open belaran@999: Publication-licensed work to its author(s). belaran@999: belaran@999: belaran@999: belaran@999: belaran@999: License options belaran@999: belaran@999: The author(s) and/or publisher of an Open belaran@999: Publication-licensed document may elect certain options by belaran@999: appending language to the reference to or copy of the license. belaran@999: These options are considered part of the license instance and belaran@999: must be included with the license (or its incorporation by belaran@999: reference) in derived works. belaran@999: belaran@999: belaran@999: To prohibit distribution of substantively belaran@999: modified versions without the explicit permission of the belaran@999: author(s). Substantive modification is belaran@999: defined as a change to the semantic content of the document, belaran@999: and excludes mere changes in format or typographical belaran@999: corrections. belaran@999: belaran@999: To accomplish this, add the phrase belaran@999: Distribution of substantively modified versions of belaran@999: this document is prohibited without the explicit belaran@999: permission of the copyright holder. to the license belaran@999: reference or copy. belaran@999: belaran@999: To prohibit any publication of this work or belaran@999: derivative works in whole or in part in standard (paper) belaran@999: book form for commercial purposes is prohibited unless prior belaran@999: permission is obtained from the copyright holder. belaran@999: belaran@999: To accomplish this, add the phrase belaran@999: Distribution of the work or derivative of the work in belaran@999: any standard (paper) book form is prohibited unless prior belaran@999: permission is obtained from the copyright holder. belaran@999: to the license reference or copy. belaran@999: belaran@999: belaran@999: belaran@999:
belaran@999: belaran@999: belaran@999: belaran@999: