# HG changeset patch # User Frédéric Bouquet <youshe.jaalon@gmail.com> # Date 1252505256 -7200 # Node ID f0110009e9469d8b188347354267d7633695a31c # Parent c075fb0481c07f1f8ed6502cc51c50296db5cebe french translation : sync with original files (en/ch05 to en/ch14 and appD) diff -r c075fb0481c0 -r f0110009e946 fr/appD-license.xml --- a/fr/appD-license.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/appD-license.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,185 +1,179 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Open Publication License</title> -<para>\label{cha:opl}</para> +<appendix id="cha:opl"> + <?dbhtml filename="open-publication-license.html"?> + <title>Open Publication License</title> -<para>Version 1.0, 8 June 1999</para> + <para id="x_638">Version 1.0, 8 June 1999</para> -<sect1> -<title>Requirements on both unmodified and modified versions</title> + <sect1> + <title>Requirements on both unmodified and modified + versions</title> -<para>The Open Publication works may be reproduced and distributed in whole -or in part, in any medium physical or electronic, provided that the -terms of this license are adhered to, and that this license or an -incorporation of it by reference (with any options elected by the -author(s) and/or publisher) is displayed in the reproduction.</para> + <para id="x_639">The Open Publication works may be reproduced and distributed + in whole or in part, in any medium physical or electronic, + provided that the terms of this license are adhered to, and that + this license or an incorporation of it by reference (with any + options elected by the author(s) and/or publisher) is displayed + in the reproduction.</para> -<para>Proper form for an incorporation by reference is as follows:</para> + <para id="x_63a">Proper form for an incorporation by reference is as + follows:</para> -<blockquote> -<para> Copyright (c) <emphasis>year</emphasis> by <emphasis>author's name or designee</emphasis>. This - material may be distributed only subject to the terms and conditions - set forth in the Open Publication License, v<emphasis>x.y</emphasis> or later (the - latest version is presently available at - <ulink url="http://www.opencontent.org/openpub/">http://www.opencontent.org/openpub/</ulink>).</para> -</blockquote> + <blockquote> + <para id="x_63b"> Copyright (c) <emphasis>year</emphasis> by + <emphasis>author's name or designee</emphasis>. This material + may be distributed only subject to the terms and conditions + set forth in the Open Publication License, + v<emphasis>x.y</emphasis> or later (the latest version is + presently available at <ulink + url="http://www.opencontent.org/openpub/">http://www.opencontent.org/openpub/</ulink>).</para> + </blockquote> -<para>The reference must be immediately followed with any options elected by -the author(s) and/or publisher of the document (see -section <xref linkend="sec:opl:options"/>).</para> + <para id="x_63c">The reference must be immediately followed with any options + elected by the author(s) and/or publisher of the document (see + <xref linkend="sec:opl:options"/>).</para> -<para>Commercial redistribution of Open Publication-licensed material is -permitted.</para> + <para id="x_63d">Commercial redistribution of Open Publication-licensed + material is permitted.</para> -<para>Any publication in standard (paper) book form shall require the -citation of the original publisher and author. The publisher and -author's names shall appear on all outer surfaces of the book. On all -outer surfaces of the book the original publisher's name shall be as -large as the title of the work and cited as possessive with respect to -the title.</para> + <para id="x_63e">Any publication in standard (paper) book form shall require + the citation of the original publisher and author. The publisher + and author's names shall appear on all outer surfaces of the + book. On all outer surfaces of the book the original publisher's + name shall be as large as the title of the work and cited as + possessive with respect to the title.</para> -</sect1> -<sect1> -<title>Copyright</title> + </sect1> + <sect1> + <title>Copyright</title> -<para>The copyright to each Open Publication is owned by its author(s) or -designee. -</para> + <para id="x_63f">The copyright to each Open Publication is owned by its + author(s) or designee.</para> -</sect1> -<sect1> -<title>Scope of license</title> + </sect1> + <sect1> + <title>Scope of license</title> -<para>The following license terms apply to all Open Publication works, -unless otherwise explicitly stated in the document. -</para> + <para id="x_640">The following license terms apply to all Open Publication + works, unless otherwise explicitly stated in the + document.</para> -<para>Mere aggregation of Open Publication works or a portion of an Open -Publication work with other works or programs on the same media shall -not cause this license to apply to those other works. The aggregate -work shall contain a notice specifying the inclusion of the Open -Publication material and appropriate copyright notice. -</para> + <para id="x_641">Mere aggregation of Open Publication works or a portion of + an Open Publication work with other works or programs on the + same media shall not cause this license to apply to those other + works. The aggregate work shall contain a notice specifying the + inclusion of the Open Publication material and appropriate + copyright notice.</para> -<para><emphasis role="bold">Severability</emphasis>. If any part of this license is found to be -unenforceable in any jurisdiction, the remaining portions of the -license remain in force. -</para> + <para id="x_642"><emphasis role="bold">Severability</emphasis>. If any part + of this license is found to be unenforceable in any + jurisdiction, the remaining portions of the license remain in + force.</para> -<para><emphasis role="bold">No warranty</emphasis>. Open Publication works are licensed and provided -<quote>as is</quote> without warranty of any kind, express or implied, including, -but not limited to, the implied warranties of merchantability and -fitness for a particular purpose or a warranty of non-infringement. -</para> + <para id="x_643"><emphasis role="bold">No warranty</emphasis>. Open + Publication works are licensed and provided <quote>as is</quote> + without warranty of any kind, express or implied, including, but + not limited to, the implied warranties of merchantability and + fitness for a particular purpose or a warranty of + non-infringement.</para> -</sect1> -<sect1> -<title>Requirements on modified works</title> + </sect1> + <sect1> + <title>Requirements on modified works</title> -<para>All modified versions of documents covered by this license, including -translations, anthologies, compilations and partial documents, must -meet the following requirements: -</para> + <para id="x_644">All modified versions of documents covered by this license, + including translations, anthologies, compilations and partial + documents, must meet the following requirements:</para> -<orderedlist> -<listitem><para>The modified version must be labeled as such. -</para> -</listitem> -<listitem><para>The person making the modifications must be identified and the - modifications dated. -</para> -</listitem> -<listitem><para>Acknowledgement of the original author and publisher if - applicable must be retained according to normal academic citation - practices. -</para> -</listitem> -<listitem><para>The location of the original unmodified document must be - identified. -</para> -</listitem> -<listitem><para>The original author's (or authors') name(s) may not be used to - assert or imply endorsement of the resulting document without the - original author's (or authors') permission. -</para> -</listitem></orderedlist> + <orderedlist> + <listitem><para id="x_645">The modified version must be labeled as + such.</para> + </listitem> + <listitem><para id="x_646">The person making the modifications must be + identified and the modifications dated.</para> + </listitem> + <listitem><para id="x_647">Acknowledgement of the original author and + publisher if applicable must be retained according to normal + academic citation practices.</para> + </listitem> + <listitem><para id="x_648">The location of the original unmodified document + must be identified.</para> + </listitem> + <listitem><para id="x_649">The original author's (or authors') name(s) may + not be used to assert or imply endorsement of the resulting + document without the original author's (or authors') + permission.</para> + </listitem></orderedlist> -</sect1> -<sect1> -<title>Good-practice recommendations</title> + </sect1> + <sect1> + <title>Good-practice recommendations</title> -<para>In addition to the requirements of this license, it is requested from -and strongly recommended of redistributors that: -</para> + <para id="x_64a">In addition to the requirements of this license, it is + requested from and strongly recommended of redistributors + that:</para> -<orderedlist> -<listitem><para>If you are distributing Open Publication works on hardcopy or - CD-ROM, you provide email notification to the authors of your intent - to redistribute at least thirty days before your manuscript or media - freeze, to give the authors time to provide updated documents. This - notification should describe modifications, if any, made to the - document. -</para> -</listitem> -<listitem><para>All substantive modifications (including deletions) be either - clearly marked up in the document or else described in an attachment - to the document. -</para> -</listitem> -<listitem><para>Finally, while it is not mandatory under this license, it is - considered good form to offer a free copy of any hardcopy and CD-ROM - expression of an Open Publication-licensed work to its author(s). -</para> -</listitem></orderedlist> + <orderedlist> + <listitem><para id="x_64b">If you are distributing Open Publication works + on hardcopy or CD-ROM, you provide email notification to the + authors of your intent to redistribute at least thirty days + before your manuscript or media freeze, to give the authors + time to provide updated documents. This notification should + describe modifications, if any, made to the document.</para> + </listitem> + <listitem><para id="x_64c">All substantive modifications (including + deletions) be either clearly marked up in the document or + else described in an attachment to the document.</para> + </listitem> + <listitem><para id="x_64d">Finally, while it is not mandatory under this + license, it is considered good form to offer a free copy of + any hardcopy and CD-ROM expression of an Open + Publication-licensed work to its author(s).</para> + </listitem></orderedlist> -</sect1> -<sect1> -<title>License options</title> -<para>\label{sec:opl:options} -</para> + </sect1> + <sect1 id="sec:opl:options"> + <title>License options</title> -<para>The author(s) and/or publisher of an Open Publication-licensed -document may elect certain options by appending language to the -reference to or copy of the license. These options are considered part -of the license instance and must be included with the license (or its -incorporation by reference) in derived works. -</para> + <para id="x_64e">The author(s) and/or publisher of an Open + Publication-licensed document may elect certain options by + appending language to the reference to or copy of the license. + These options are considered part of the license instance and + must be included with the license (or its incorporation by + reference) in derived works.</para> -<orderedlist> -<listitem><para>To prohibit distribution of substantively modified versions - without the explicit permission of the author(s). <quote>Substantive - modification</quote> is defined as a change to the semantic content of the - document, and excludes mere changes in format or typographical - corrections. -</para> -</listitem> -<listitem><para> To accomplish this, add the phrase <quote>Distribution of substantively - modified versions of this document is prohibited without the - explicit permission of the copyright holder.</quote> to the license - reference or copy. -</para> -</listitem> -</para> -</listitem> -<listitem><para>To prohibit any publication of this work or derivative works in - whole or in part in standard (paper) book form for commercial - purposes is prohibited unless prior permission is obtained from the - copyright holder. -</para> -</listitem> -<listitem><para> To accomplish this, add the phrase <quote>Distribution of the work or - derivative of the work in any standard (paper) book form is - prohibited unless prior permission is obtained from the copyright - holder.</quote> to the license reference or copy. -</para> -</listitem></orderedlist> + <orderedlist> + <listitem><para id="x_64f">To prohibit distribution of substantively + modified versions without the explicit permission of the + author(s). <quote>Substantive modification</quote> is + defined as a change to the semantic content of the document, + and excludes mere changes in format or typographical + corrections.</para> + </listitem> + <listitem><para id="x_650"> To accomplish this, add the phrase + <quote>Distribution of substantively modified versions of + this document is prohibited without the explicit + permission of the copyright holder.</quote> to the license + reference or copy.</para> + </listitem> + <listitem><para id="x_651">To prohibit any publication of this work or + derivative works in whole or in part in standard (paper) + book form for commercial purposes is prohibited unless prior + permission is obtained from the copyright holder.</para> + </listitem> + <listitem><para id="x_652">To accomplish this, add the phrase + <quote>Distribution of the work or derivative of the work in + any standard (paper) book form is prohibited unless prior + permission is obtained from the copyright holder.</quote> + to the license reference or copy.</para> + </listitem></orderedlist> -</sect1> -</chapter> + </sect1> +</appendix> <!-- local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") +sgml-parent-document: ("00book.xml" "book" "appendix") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch05-daily.xml --- a/fr/ch05-daily.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch05-daily.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,474 +1,840 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Mercurial in daily use</title> -<para>\label{chap:daily}</para> - -<sect1> -<title>Telling Mercurial which files to track</title> - -<para>Mercurial does not work with files in your repository unless you tell -it to manage them. The <command role="hg-cmd">hg status</command> command will tell you which -files Mercurial doesn't know about; it uses a <quote><literal>?</literal></quote> to -display such files.</para> - -<para>To tell Mercurial to track a file, use the <command role="hg-cmd">hg add</command> command. Once -you have added a file, the entry in the output of <command role="hg-cmd">hg status</command> for -that file changes from <quote><literal>?</literal></quote> to <quote><literal>A</literal></quote>. -<!-- &interaction.daily.files.add; --></para> - -<para>After you run a <command role="hg-cmd">hg commit</command>, the files that you added before the -commit will no longer be listed in the output of <command role="hg-cmd">hg status</command>. The -reason for this is that <command role="hg-cmd">hg status</command> only tells you about -<quote>interesting</quote> files&emdash;those that you have modified or told Mercurial -to do something with&emdash;by default. If you have a repository that -contains thousands of files, you will rarely want to know about files -that Mercurial is tracking, but that have not changed. (You can still -get this information; we'll return to this later.)</para> - -<para>Once you add a file, Mercurial doesn't do anything with it -immediately. Instead, it will take a snapshot of the file's state the -next time you perform a commit. It will then continue to track the -changes you make to the file every time you commit, until you remove -the file.</para> - -<sect2> -<title>Explicit versus implicit file naming</title> - -<para>A useful behaviour that Mercurial has is that if you pass the name of -a directory to a command, every Mercurial command will treat this as -<quote>I want to operate on every file in this directory and its -subdirectories</quote>. -<!-- &interaction.daily.files.add-dir; --> -Notice in this example that Mercurial printed the names of the files -it added, whereas it didn't do so when we added the file named -<filename>a</filename> in the earlier example.</para> - -<para>What's going on is that in the former case, we explicitly named the -file to add on the command line, so the assumption that Mercurial -makes in such cases is that you know what you were doing, and it -doesn't print any output.</para> - -<para>However, when we <emphasis>imply</emphasis> the names of files by giving the name of -a directory, Mercurial takes the extra step of printing the name of -each file that it does something with. This makes it more clear what -is happening, and reduces the likelihood of a silent and nasty -surprise. This behaviour is common to most Mercurial commands.</para> - -</sect2> -<sect2> -<title>Aside: Mercurial tracks files, not directories</title> - -<para>Mercurial does not track directory information. Instead, it tracks -the path to a file. Before creating a file, it first creates any -missing directory components of the path. After it deletes a file, it -then deletes any empty directories that were in the deleted file's -path. This sounds like a trivial distinction, but it has one minor -practical consequence: it is not possible to represent a completely -empty directory in Mercurial. -</para> - -<para>Empty directories are rarely useful, and there are unintrusive -workarounds that you can use to achieve an appropriate effect. The -developers of Mercurial thus felt that the complexity that would be -required to manage empty directories was not worth the limited benefit -this feature would bring. -</para> - -<para>If you need an empty directory in your repository, there are a few -ways to achieve this. One is to create a directory, then <command role="hg-cmd">hg add</command> a -<quote>hidden</quote> file to that directory. On Unix-like systems, any file -name that begins with a period (<quote><literal>.</literal></quote>) is treated as hidden -by most commands and GUI tools. This approach is illustrated in -figure <xref linkend="ex:daily:hidden"/>. -</para> - -<informalfigure> -<para> <!-- &interaction.daily.files.hidden; --> - <caption><para>Simulating an empty directory using a hidden file</para></caption> - \label{ex:daily:hidden} -</para> -</informalfigure> - -<para>Another way to tackle a need for an empty directory is to simply -create one in your automated build scripts before they will need it. -</para> - -</sect2> -</sect1> -<sect1> -<title>How to stop tracking a file</title> - -<para>Once you decide that a file no longer belongs in your repository, use -the <command role="hg-cmd">hg remove</command> command; this deletes the file, and tells Mercurial -to stop tracking it. A removed file is represented in the output of -<command role="hg-cmd">hg status</command> with a <quote><literal>R</literal></quote>. -<!-- &interaction.daily.files.remove; --> -</para> - -<para>After you <command role="hg-cmd">hg remove</command> a file, Mercurial will no longer track -changes to that file, even if you recreate a file with the same name -in your working directory. If you do recreate a file with the same -name and want Mercurial to track the new file, simply <command role="hg-cmd">hg add</command> it. -Mercurial will know that the newly added file is not related to the -old file of the same name. -</para> - -<sect2> -<title>Removing a file does not affect its history</title> - -<para>It is important to understand that removing a file has only two -effects. -</para> -<itemizedlist> -<listitem><para>It removes the current version of the file from the working - directory. -</para> -</listitem> -<listitem><para>It stops Mercurial from tracking changes to the file, from the - time of the next commit. -</para> -</listitem></itemizedlist> -<para>Removing a file <emphasis>does not</emphasis> in any way alter the <emphasis>history</emphasis> of -the file. -</para> - -<para>If you update the working directory to a changeset in which a file -that you have removed was still tracked, it will reappear in the -working directory, with the contents it had when you committed that -changeset. If you then update the working directory to a later -changeset, in which the file had been removed, Mercurial will once -again remove the file from the working directory. -</para> - -</sect2> -<sect2> -<title>Missing files</title> - -<para>Mercurial considers a file that you have deleted, but not used -<command role="hg-cmd">hg remove</command> to delete, to be <emphasis>missing</emphasis>. A missing file is -represented with <quote><literal>!</literal></quote> in the output of <command role="hg-cmd">hg status</command>. -Mercurial commands will not generally do anything with missing files. -<!-- &interaction.daily.files.missing; --> -</para> - -<para>If your repository contains a file that <command role="hg-cmd">hg status</command> reports as -missing, and you want the file to stay gone, you can run -<command role="hg-cmd">hg remove <option role="hg-opt-remove">--after</option></command> at any time later on, to -tell Mercurial that you really did mean to remove the file. -<!-- &interaction.daily.files.remove-after; --> -</para> - -<para>On the other hand, if you deleted the missing file by accident, use -<command role="hg-cmd">hg revert <emphasis>filename</emphasis></command> to recover the file. It will -reappear, in unmodified form. -<!-- &interaction.daily.files.recover-missing; --> -</para> - -<para>\subsection{Aside: why tell Mercurial explicitly to - remove a file?} -</para> - -<para>You might wonder why Mercurial requires you to explicitly tell it that -you are deleting a file. Early during the development of Mercurial, -it let you delete a file however you pleased; Mercurial would notice -the absence of the file automatically when you next ran a -<command role="hg-cmd">hg commit</command>, and stop tracking the file. In practice, this made it -too easy to accidentally remove a file without noticing. -</para> - -<para>\subsection{Useful shorthand&emdash;adding and removing files - in one step} -</para> - -<para>Mercurial offers a combination command, <command role="hg-cmd">hg addremove</command>, that adds -untracked files and marks missing files as removed. -<!-- &interaction.daily.files.addremove; --> -The <command role="hg-cmd">hg commit</command> command also provides a <option role="hg-opt-commit">-A</option> option -that performs this same add-and-remove, immediately followed by a -commit. -<!-- &interaction.daily.files.commit-addremove; --> -</para> - -</sect2> -</sect1> -<sect1> -<title>Copying files</title> - -<para>Mercurial provides a <command role="hg-cmd">hg copy</command> command that lets you make a new -copy of a file. When you copy a file using this command, Mercurial -makes a record of the fact that the new file is a copy of the original -file. It treats these copied files specially when you merge your work -with someone else's. -</para> - -<sect2> -<title>The results of copying during a merge</title> - -<para>What happens during a merge is that changes <quote>follow</quote> a copy. To -best illustrate what this means, let's create an example. We'll start -with the usual tiny repository that contains a single file. -<!-- &interaction.daily.copy.init; --> -We need to do some work in parallel, so that we'll have something to -merge. So let's clone our repository. -<!-- &interaction.daily.copy.clone; --> -Back in our initial repository, let's use the <command role="hg-cmd">hg copy</command> command to -make a copy of the first file we created. -<!-- &interaction.daily.copy.copy; --> -</para> - -<para>If we look at the output of the <command role="hg-cmd">hg status</command> command afterwards, the -copied file looks just like a normal added file. -<!-- &interaction.daily.copy.status; --> -But if we pass the <option role="hg-opt-status">-C</option> option to <command role="hg-cmd">hg status</command>, it -prints another line of output: this is the file that our newly-added -file was copied <emphasis>from</emphasis>. -<!-- &interaction.daily.copy.status-copy; --> -</para> - -<para>Now, back in the repository we cloned, let's make a change in -parallel. We'll add a line of content to the original file that we -created. -<!-- &interaction.daily.copy.other; --> -Now we have a modified <filename>file</filename> in this repository. When we -pull the changes from the first repository, and merge the two heads, -Mercurial will propagate the changes that we made locally to -<filename>file</filename> into its copy, <filename>new-file</filename>. -<!-- &interaction.daily.copy.merge; --> -</para> - -</sect2> -<sect2> -<title>Why should changes follow copies?</title> -<para>\label{sec:daily:why-copy} -</para> - -<para>This behaviour, of changes to a file propagating out to copies of the -file, might seem esoteric, but in most cases it's highly desirable. -</para> - -<para>First of all, remember that this propagation <emphasis>only</emphasis> happens when -you merge. So if you <command role="hg-cmd">hg copy</command> a file, and subsequently modify the -original file during the normal course of your work, nothing will -happen. -</para> - -<para>The second thing to know is that modifications will only propagate -across a copy as long as the repository that you're pulling changes -from <emphasis>doesn't know</emphasis> about the copy. -</para> - -<para>The reason that Mercurial does this is as follows. Let's say I make -an important bug fix in a source file, and commit my changes. -Meanwhile, you've decided to <command role="hg-cmd">hg copy</command> the file in your repository, -without knowing about the bug or having seen the fix, and you have -started hacking on your copy of the file. -</para> - -<para>If you pulled and merged my changes, and Mercurial <emphasis>didn't</emphasis> -propagate changes across copies, your source file would now contain -the bug, and unless you remembered to propagate the bug fix by hand, -the bug would <emphasis>remain</emphasis> in your copy of the file. -</para> - -<para>By automatically propagating the change that fixed the bug from the -original file to the copy, Mercurial prevents this class of problem. -To my knowledge, Mercurial is the <emphasis>only</emphasis> revision control system -that propagates changes across copies like this. -</para> - -<para>Once your change history has a record that the copy and subsequent -merge occurred, there's usually no further need to propagate changes -from the original file to the copied file, and that's why Mercurial -only propagates changes across copies until this point, and no -further. -</para> - -</sect2> -<sect2> -<title>How to make changes <emphasis>not</emphasis> follow a copy</title> - -<para>If, for some reason, you decide that this business of automatically -propagating changes across copies is not for you, simply use your -system's normal file copy command (on Unix-like systems, that's -<command>cp</command>) to make a copy of a file, then <command role="hg-cmd">hg add</command> the new copy -by hand. Before you do so, though, please do reread -section <xref linkend="sec:daily:why-copy"/>, and make an informed decision that -this behaviour is not appropriate to your specific case. -</para> - -</sect2> -<sect2> -<title>Behaviour of the <command role="hg-cmd">hg copy</command> command</title> - -<para>When you use the <command role="hg-cmd">hg copy</command> command, Mercurial makes a copy of each -source file as it currently stands in the working directory. This -means that if you make some modifications to a file, then <command role="hg-cmd">hg copy</command> -it without first having committed those changes, the new copy will -also contain the modifications you have made up until that point. (I -find this behaviour a little counterintuitive, which is why I mention -it here.) -</para> - -<para>The <command role="hg-cmd">hg copy</command> command acts similarly to the Unix <command>cp</command> -command (you can use the <command role="hg-cmd">hg cp</command> alias if you prefer). The last -argument is the <emphasis>destination</emphasis>, and all prior arguments are -<emphasis>sources</emphasis>. If you pass it a single file as the source, and the -destination does not exist, it creates a new file with that name. -<!-- &interaction.daily.copy.simple; --> -If the destination is a directory, Mercurial copies its sources into -that directory. -<!-- &interaction.daily.copy.dir-dest; --> -Copying a directory is recursive, and preserves the directory -structure of the source. -<!-- &interaction.daily.copy.dir-src; --> -If the source and destination are both directories, the source tree is -recreated in the destination directory. -<!-- &interaction.daily.copy.dir-src-dest; --> -</para> - -<para>As with the <command role="hg-cmd">hg rename</command> command, if you copy a file manually and -then want Mercurial to know that you've copied the file, simply use -the <option role="hg-opt-copy">--after</option> option to <command role="hg-cmd">hg copy</command>. -<!-- &interaction.daily.copy.after; --> -</para> - -</sect2> -</sect1> -<sect1> -<title>Renaming files</title> - -<para>It's rather more common to need to rename a file than to make a copy -of it. The reason I discussed the <command role="hg-cmd">hg copy</command> command before talking -about renaming files is that Mercurial treats a rename in essentially -the same way as a copy. Therefore, knowing what Mercurial does when -you copy a file tells you what to expect when you rename a file. -</para> - -<para>When you use the <command role="hg-cmd">hg rename</command> command, Mercurial makes a copy of -each source file, then deletes it and marks the file as removed. -<!-- &interaction.daily.rename.rename; --> -The <command role="hg-cmd">hg status</command> command shows the newly copied file as added, and -the copied-from file as removed. -<!-- &interaction.daily.rename.status; --> -As with the results of a <command role="hg-cmd">hg copy</command>, we must use the -<option role="hg-opt-status">-C</option> option to <command role="hg-cmd">hg status</command> to see that the added file -is really being tracked by Mercurial as a copy of the original, now -removed, file. -<!-- &interaction.daily.rename.status-copy; --> -</para> - -<para>As with <command role="hg-cmd">hg remove</command> and <command role="hg-cmd">hg copy</command>, you can tell Mercurial about -a rename after the fact using the <option role="hg-opt-rename">--after</option> option. In -most other respects, the behaviour of the <command role="hg-cmd">hg rename</command> command, and -the options it accepts, are similar to the <command role="hg-cmd">hg copy</command> command. -</para> - -<sect2> -<title>Renaming files and merging changes</title> - -<para>Since Mercurial's rename is implemented as copy-and-remove, the same -propagation of changes happens when you merge after a rename as after -a copy. -</para> - -<para>If I modify a file, and you rename it to a new name, and then we merge -our respective changes, my modifications to the file under its -original name will be propagated into the file under its new name. -(This is something you might expect to <quote>simply work,</quote> but not all -revision control systems actually do this.) -</para> - -<para>Whereas having changes follow a copy is a feature where you can -perhaps nod and say <quote>yes, that might be useful,</quote> it should be clear -that having them follow a rename is definitely important. Without -this facility, it would simply be too easy for changes to become -orphaned when files are renamed. -</para> - -</sect2> -<sect2> -<title>Divergent renames and merging</title> - -<para>The case of diverging names occurs when two developers start with a -file&emdash;let's call it <filename>foo</filename>&emdash;in their respective -repositories. -</para> - -<para><!-- &interaction.rename.divergent.clone; --> -Anne renames the file to <filename>bar</filename>. -<!-- &interaction.rename.divergent.rename.anne; --> -Meanwhile, Bob renames it to <filename>quux</filename>. -<!-- &interaction.rename.divergent.rename.bob; --> -</para> - -<para>I like to think of this as a conflict because each developer has -expressed different intentions about what the file ought to be named. -</para> - -<para>What do you think should happen when they merge their work? -Mercurial's actual behaviour is that it always preserves <emphasis>both</emphasis> -names when it merges changesets that contain divergent renames. -<!-- &interaction.rename.divergent.merge; --> -</para> - -<para>Notice that Mercurial does warn about the divergent renames, but it -leaves it up to you to do something about the divergence after the merge. -</para> - -</sect2> -<sect2> -<title>Convergent renames and merging</title> - -<para>Another kind of rename conflict occurs when two people choose to -rename different <emphasis>source</emphasis> files to the same <emphasis>destination</emphasis>. -In this case, Mercurial runs its normal merge machinery, and lets you -guide it to a suitable resolution. -</para> - -</sect2> -<sect2> -<title>Other name-related corner cases</title> - -<para>Mercurial has a longstanding bug in which it fails to handle a merge -where one side has a file with a given name, while another has a -directory with the same name. This is documented as <ulink role="hg-bug" url="http://www.selenic.com/mercurial/bts/issue29">issue 29</ulink>. -<!-- &interaction.issue29.go; --> -</para> - -</sect2> -</sect1> -<sect1> -<title>Recovering from mistakes</title> - -<para>Mercurial has some useful commands that will help you to recover from -some common mistakes. -</para> - -<para>The <command role="hg-cmd">hg revert</command> command lets you undo changes that you have made to -your working directory. For example, if you <command role="hg-cmd">hg add</command> a file by -accident, just run <command role="hg-cmd">hg revert</command> with the name of the file you added, -and while the file won't be touched in any way, it won't be tracked -for adding by Mercurial any longer, either. You can also use -<command role="hg-cmd">hg revert</command> to get rid of erroneous changes to a file. -</para> - -<para>It's useful to remember that the <command role="hg-cmd">hg revert</command> command is useful for -changes that you have not yet committed. Once you've committed a -change, if you decide it was a mistake, you can still do something -about it, though your options may be more limited. -</para> - -<para>For more information about the <command role="hg-cmd">hg revert</command> command, and details -about how to deal with changes you have already committed, see -chapter <xref linkend="chap:undo"/>. -</para> - -</sect1> +<chapter id="chap:daily"> + <?dbhtml filename="mercurial-in-daily-use.html"?> + <title>Mercurial in daily use</title> + + <sect1> + <title>Telling Mercurial which files to track</title> + + <para id="x_1a3">Mercurial does not work with files in your repository unless + you tell it to manage them. The <command role="hg-cmd">hg + status</command> command will tell you which files Mercurial + doesn't know about; it uses a + <quote><literal>?</literal></quote> to display such + files.</para> + + <para id="x_1a4">To tell Mercurial to track a file, use the <command + role="hg-cmd">hg add</command> command. Once you have added a + file, the entry in the output of <command role="hg-cmd">hg + status</command> for that file changes from + <quote><literal>?</literal></quote> to + <quote><literal>A</literal></quote>.</para> + + &interaction.daily.files.add; + + <para id="x_1a5">After you run a <command role="hg-cmd">hg commit</command>, + the files that you added before the commit will no longer be + listed in the output of <command role="hg-cmd">hg + status</command>. The reason for this is that by default, <command + role="hg-cmd">hg status</command> only tells you about + <quote>interesting</quote> files&emdash;those that you have (for + example) modified, removed, or renamed. If you have a repository + that contains thousands of files, you will rarely want to know + about files that Mercurial is tracking, but that have not + changed. (You can still get this information; we'll return to + this later.)</para> + + <para id="x_1a6">Once you add a file, Mercurial doesn't do anything with it + immediately. Instead, it will take a snapshot of the file's + state the next time you perform a commit. It will then continue + to track the changes you make to the file every time you commit, + until you remove the file.</para> + + <sect2> + <title>Explicit versus implicit file naming</title> + + <para id="x_1a7">A useful behavior that Mercurial has is that if you pass + the name of a directory to a command, every Mercurial command + will treat this as <quote>I want to operate on every file in + this directory and its subdirectories</quote>.</para> + + &interaction.daily.files.add-dir; + + <para id="x_1a8">Notice in this example that Mercurial printed + the names of the files it added, whereas it didn't do so when + we added the file named <filename>myfile.txt</filename> in the + earlier example.</para> + + <para id="x_1a9">What's going on is that in the former case, we explicitly + named the file to add on the command line. The assumption + that Mercurial makes in such cases is that we know what we + are doing, and it doesn't print any output.</para> + + <para id="x_1aa">However, when we <emphasis>imply</emphasis> the names of + files by giving the name of a directory, Mercurial takes the + extra step of printing the name of each file that it does + something with. This makes it more clear what is happening, + and reduces the likelihood of a silent and nasty surprise. + This behavior is common to most Mercurial commands.</para> + </sect2> + + <sect2> + <title>Mercurial tracks files, not directories</title> + + <para id="x_1ab">Mercurial does not track directory information. Instead, + it tracks the path to a file. Before creating a file, it + first creates any missing directory components of the path. + After it deletes a file, it then deletes any empty directories + that were in the deleted file's path. This sounds like a + trivial distinction, but it has one minor practical + consequence: it is not possible to represent a completely + empty directory in Mercurial.</para> + + <para id="x_1ac">Empty directories are rarely useful, and there are + unintrusive workarounds that you can use to achieve an + appropriate effect. The developers of Mercurial thus felt + that the complexity that would be required to manage empty + directories was not worth the limited benefit this feature + would bring.</para> + + <para id="x_1ad">If you need an empty directory in your repository, there + are a few ways to achieve this. One is to create a directory, + then <command role="hg-cmd">hg add</command> a + <quote>hidden</quote> file to that directory. On Unix-like + systems, any file name that begins with a period + (<quote><literal>.</literal></quote>) is treated as hidden by + most commands and GUI tools. This approach is illustrated + below.</para> + +&interaction.daily.files.hidden; + + <para id="x_1ae">Another way to tackle a need for an empty directory is to + simply create one in your automated build scripts before they + will need it.</para> + </sect2> + </sect1> + + <sect1> + <title>How to stop tracking a file</title> + + <para id="x_1af">Once you decide that a file no longer belongs in + your repository, use the <command role="hg-cmd">hg + remove</command> command. This deletes the file, and tells + Mercurial to stop tracking it (which will occur at the next + commit). A removed file is represented in the output of + <command role="hg-cmd">hg status</command> with a + <quote><literal>R</literal></quote>.</para> + + &interaction.daily.files.remove; + + <para id="x_1b0">After you <command role="hg-cmd">hg remove</command> a file, + Mercurial will no longer track changes to that file, even if you + recreate a file with the same name in your working directory. + If you do recreate a file with the same name and want Mercurial + to track the new file, simply <command role="hg-cmd">hg + add</command> it. Mercurial will know that the newly added + file is not related to the old file of the same name.</para> + + <sect2> + <title>Removing a file does not affect its history</title> + + <para id="x_1b1">It is important to understand that removing a file has + only two effects.</para> + <itemizedlist> + <listitem><para id="x_1b2">It removes the current version of the file + from the working directory.</para> + </listitem> + <listitem><para id="x_1b3">It stops Mercurial from tracking changes to + the file, from the time of the next commit.</para> + </listitem></itemizedlist> + <para id="x_1b4">Removing a file <emphasis>does not</emphasis> in any way + alter the <emphasis>history</emphasis> of the file.</para> + + <para id="x_1b5">If you update the working directory to a + changeset that was committed when it was still tracking a file + that you later removed, the file will reappear in the working + directory, with the contents it had when you committed that + changeset. If you then update the working directory to a + later changeset, in which the file had been removed, Mercurial + will once again remove the file from the working + directory.</para> + </sect2> + + <sect2> + <title>Missing files</title> + + <para id="x_1b6">Mercurial considers a file that you have deleted, but not + used <command role="hg-cmd">hg remove</command> to delete, to + be <emphasis>missing</emphasis>. A missing file is + represented with <quote><literal>!</literal></quote> in the + output of <command role="hg-cmd">hg status</command>. + Mercurial commands will not generally do anything with missing + files.</para> + + &interaction.daily.files.missing; + + <para id="x_1b7">If your repository contains a file that <command + role="hg-cmd">hg status</command> reports as missing, and + you want the file to stay gone, you can run <command + role="hg-cmd">hg remove <option + role="hg-opt-remove">--after</option></command> at any + time later on, to tell Mercurial that you really did mean to + remove the file.</para> + + &interaction.daily.files.remove-after; + + <para id="x_1b8">On the other hand, if you deleted the missing file by + accident, give <command role="hg-cmd">hg revert</command> the + name of the file to recover. It will reappear, in unmodified + form.</para> + + &interaction.daily.files.recover-missing; + </sect2> + + <sect2> + <title>Aside: why tell Mercurial explicitly to remove a + file?</title> + + <para id="x_1b9">You might wonder why Mercurial requires you to explicitly + tell it that you are deleting a file. Early during the + development of Mercurial, it let you delete a file however you + pleased; Mercurial would notice the absence of the file + automatically when you next ran a <command role="hg-cmd">hg + commit</command>, and stop tracking the file. In practice, + this made it too easy to accidentally remove a file without + noticing.</para> + </sect2> + + <sect2> + <title>Useful shorthand&emdash;adding and removing files in one + step</title> + + <para id="x_1ba">Mercurial offers a combination command, <command + role="hg-cmd">hg addremove</command>, that adds untracked + files and marks missing files as removed.</para> + + &interaction.daily.files.addremove; + + <para id="x_1bb">The <command role="hg-cmd">hg commit</command> command + also provides a <option role="hg-opt-commit">-A</option> + option that performs this same add-and-remove, immediately + followed by a commit.</para> + + &interaction.daily.files.commit-addremove; + </sect2> + </sect1> + + <sect1 id="chap:daily.copy"> + <title>Copying files</title> + + <para id="x_1bc">Mercurial provides a <command role="hg-cmd">hg + copy</command> command that lets you make a new copy of a + file. When you copy a file using this command, Mercurial makes + a record of the fact that the new file is a copy of the original + file. It treats these copied files specially when you merge + your work with someone else's.</para> + + <sect2> + <title>The results of copying during a merge</title> + + <para id="x_1bd">What happens during a merge is that changes + <quote>follow</quote> a copy. To best illustrate what this + means, let's create an example. We'll start with the usual + tiny repository that contains a single file.</para> + + &interaction.daily.copy.init; + + <para id="x_1be">We need to do some work in + parallel, so that we'll have something to merge. So let's + clone our repository.</para> + + &interaction.daily.copy.clone; + + <para id="x_1bf">Back in our initial repository, let's use the <command + role="hg-cmd">hg copy</command> command to make a copy of + the first file we created.</para> + + &interaction.daily.copy.copy; + + <para id="x_1c0">If we look at the output of the <command role="hg-cmd">hg + status</command> command afterwards, the copied file looks + just like a normal added file.</para> + + &interaction.daily.copy.status; + + <para id="x_1c1">But if we pass the <option + role="hg-opt-status">-C</option> option to <command + role="hg-cmd">hg status</command>, it prints another line of + output: this is the file that our newly-added file was copied + <emphasis>from</emphasis>.</para> + + &interaction.daily.copy.status-copy; + + <para id="x_1c2">Now, back in the repository we cloned, let's make a change + in parallel. We'll add a line of content to the original file + that we created.</para> + + &interaction.daily.copy.other; + + <para id="x_1c3">Now we have a modified <filename>file</filename> in this + repository. When we pull the changes from the first + repository, and merge the two heads, Mercurial will propagate + the changes that we made locally to <filename>file</filename> + into its copy, <filename>new-file</filename>.</para> + + &interaction.daily.copy.merge; + </sect2> + + <sect2 id="sec:daily:why-copy"> + <title>Why should changes follow copies?</title> + + <para id="x_1c4">This behavior&emdash;of changes to a file + propagating out to copies of the file&emdash;might seem + esoteric, but in most cases it's highly desirable.</para> + + <para id="x_1c5">First of all, remember that this propagation + <emphasis>only</emphasis> happens when you merge. So if you + <command role="hg-cmd">hg copy</command> a file, and + subsequently modify the original file during the normal course + of your work, nothing will happen.</para> + + <para id="x_1c6">The second thing to know is that modifications will only + propagate across a copy as long as the changeset that you're + merging changes from <emphasis>hasn't yet seen</emphasis> + the copy.</para> + + <para id="x_1c7">The reason that Mercurial does this is as follows. Let's + say I make an important bug fix in a source file, and commit + my changes. Meanwhile, you've decided to <command + role="hg-cmd">hg copy</command> the file in your repository, + without knowing about the bug or having seen the fix, and you + have started hacking on your copy of the file.</para> + + <para id="x_1c8">If you pulled and merged my changes, and Mercurial + <emphasis>didn't</emphasis> propagate changes across copies, + your new source file would now contain the bug, and unless you + knew to propagate the bug fix by hand, the bug would + <emphasis>remain</emphasis> in your copy of the file.</para> + + <para id="x_1c9">By automatically propagating the change that fixed the bug + from the original file to the copy, Mercurial prevents this + class of problem. To my knowledge, Mercurial is the + <emphasis>only</emphasis> revision control system that + propagates changes across copies like this.</para> + + <para id="x_1ca">Once your change history has a record that the copy and + subsequent merge occurred, there's usually no further need to + propagate changes from the original file to the copied file, + and that's why Mercurial only propagates changes across copies + at the first merge, and not afterwards.</para> + </sect2> + + <sect2> + <title>How to make changes <emphasis>not</emphasis> follow a + copy</title> + + <para id="x_1cb">If, for some reason, you decide that this business of + automatically propagating changes across copies is not for + you, simply use your system's normal file copy command (on + Unix-like systems, that's <command>cp</command>) to make a + copy of a file, then <command role="hg-cmd">hg add</command> + the new copy by hand. Before you do so, though, please do + reread <xref linkend="sec:daily:why-copy"/>, and make + an informed + decision that this behavior is not appropriate to your + specific case.</para> + + </sect2> + <sect2> + <title>Behavior of the <command role="hg-cmd">hg copy</command> + command</title> + + <para id="x_1cc">When you use the <command role="hg-cmd">hg copy</command> + command, Mercurial makes a copy of each source file as it + currently stands in the working directory. This means that if + you make some modifications to a file, then <command + role="hg-cmd">hg copy</command> it without first having + committed those changes, the new copy will also contain the + modifications you have made up until that point. (I find this + behavior a little counterintuitive, which is why I mention it + here.)</para> + + <para id="x_1cd">The <command role="hg-cmd">hg copy</command> + command acts similarly to the Unix <command>cp</command> + command (you can use the <command role="hg-cmd">hg + cp</command> alias if you prefer). We must supply two or + more arguments, of which the last is treated as the + <emphasis>destination</emphasis>, and all others are + <emphasis>sources</emphasis>.</para> + + <para id="x_685">If you pass <command role="hg-cmd">hg copy</command> a + single file as the source, and the destination does not exist, + it creates a new file with that name.</para> + + &interaction.daily.copy.simple; + + <para id="x_1ce">If the destination is a directory, Mercurial copies its + sources into that directory.</para> + + &interaction.daily.copy.dir-dest; + + <para id="x_1cf">Copying a directory is + recursive, and preserves the directory structure of the + source.</para> + + &interaction.daily.copy.dir-src; + + <para id="x_1d0">If the source and destination are both directories, the + source tree is recreated in the destination directory.</para> + + &interaction.daily.copy.dir-src-dest; + + <para id="x_1d1">As with the <command role="hg-cmd">hg remove</command> + command, if you copy a file manually and then want Mercurial + to know that you've copied the file, simply use the <option + role="hg-opt-copy">--after</option> option to <command + role="hg-cmd">hg copy</command>.</para> + + &interaction.daily.copy.after; + </sect2> + </sect1> + + <sect1> + <title>Renaming files</title> + + <para id="x_1d2">It's rather more common to need to rename a file than to + make a copy of it. The reason I discussed the <command + role="hg-cmd">hg copy</command> command before talking about + renaming files is that Mercurial treats a rename in essentially + the same way as a copy. Therefore, knowing what Mercurial does + when you copy a file tells you what to expect when you rename a + file.</para> + + <para id="x_1d3">When you use the <command role="hg-cmd">hg rename</command> + command, Mercurial makes a copy of each source file, then + deletes it and marks the file as removed.</para> + + &interaction.daily.rename.rename; + + <para id="x_1d4">The <command role="hg-cmd">hg status</command> command shows + the newly copied file as added, and the copied-from file as + removed.</para> + + &interaction.daily.rename.status; + + <para id="x_1d5">As with the results of a <command role="hg-cmd">hg + copy</command>, we must use the <option + role="hg-opt-status">-C</option> option to <command + role="hg-cmd">hg status</command> to see that the added file + is really being tracked by Mercurial as a copy of the original, + now removed, file.</para> + + &interaction.daily.rename.status-copy; + + <para id="x_1d6">As with <command role="hg-cmd">hg remove</command> and + <command role="hg-cmd">hg copy</command>, you can tell Mercurial + about a rename after the fact using the <option + role="hg-opt-rename">--after</option> option. In most other + respects, the behavior of the <command role="hg-cmd">hg + rename</command> command, and the options it accepts, are + similar to the <command role="hg-cmd">hg copy</command> + command.</para> + + <para id="x_686">If you're familiar with the Unix command line, you'll be + glad to know that <command role="hg-cmd">hg rename</command> + command can be invoked as <command role="hg-cmd">hg + mv</command>.</para> + + <sect2> + <title>Renaming files and merging changes</title> + + <para id="x_1d7">Since Mercurial's rename is implemented as + copy-and-remove, the same propagation of changes happens when + you merge after a rename as after a copy.</para> + + <para id="x_1d8">If I modify a file, and you rename it to a new name, and + then we merge our respective changes, my modifications to the + file under its original name will be propagated into the file + under its new name. (This is something you might expect to + <quote>simply work,</quote> but not all revision control + systems actually do this.)</para> + + <para id="x_1d9">Whereas having changes follow a copy is a feature where + you can perhaps nod and say <quote>yes, that might be + useful,</quote> it should be clear that having them follow a + rename is definitely important. Without this facility, it + would simply be too easy for changes to become orphaned when + files are renamed.</para> + </sect2> + + <sect2> + <title>Divergent renames and merging</title> + + <para id="x_1da">The case of diverging names occurs when two developers + start with a file&emdash;let's call it + <filename>foo</filename>&emdash;in their respective + repositories.</para> + + &interaction.rename.divergent.clone; + + <para id="x_1db">Anne renames the file to <filename>bar</filename>.</para> + + &interaction.rename.divergent.rename.anne; + + <para id="x_1dc">Meanwhile, Bob renames it to + <filename>quux</filename>. (Remember that <command + role="hg-cmd">hg mv</command> is an alias for <command + role="hg-cmd">hg rename</command>.)</para> + + &interaction.rename.divergent.rename.bob; + + <para id="x_1dd">I like to think of this as a conflict because each + developer has expressed different intentions about what the + file ought to be named.</para> + + <para id="x_1de">What do you think should happen when they merge their + work? Mercurial's actual behavior is that it always preserves + <emphasis>both</emphasis> names when it merges changesets that + contain divergent renames.</para> + + &interaction.rename.divergent.merge; + + <para id="x_1df">Notice that while Mercurial warns about the divergent + renames, it leaves it up to you to do something about the + divergence after the merge.</para> + </sect2> + + <sect2> + <title>Convergent renames and merging</title> + + <para id="x_1e0">Another kind of rename conflict occurs when two people + choose to rename different <emphasis>source</emphasis> files + to the same <emphasis>destination</emphasis>. In this case, + Mercurial runs its normal merge machinery, and lets you guide + it to a suitable resolution.</para> + </sect2> + + <sect2> + <title>Other name-related corner cases</title> + + <para id="x_1e1">Mercurial has a longstanding bug in which it fails to + handle a merge where one side has a file with a given name, + while another has a directory with the same name. This is + documented as <ulink role="hg-bug" + url="http://www.selenic.com/mercurial/bts/issue29">issue + 29</ulink>.</para> + + &interaction.issue29.go; + + </sect2> + </sect1> + + <sect1> + <title>Recovering from mistakes</title> + + <para id="x_1e2">Mercurial has some useful commands that will help you to + recover from some common mistakes.</para> + + <para id="x_1e3">The <command role="hg-cmd">hg revert</command> command lets + you undo changes that you have made to your working directory. + For example, if you <command role="hg-cmd">hg add</command> a + file by accident, just run <command role="hg-cmd">hg + revert</command> with the name of the file you added, and + while the file won't be touched in any way, it won't be tracked + for adding by Mercurial any longer, either. You can also use + <command role="hg-cmd">hg revert</command> to get rid of + erroneous changes to a file.</para> + + <para id="x_1e4">It is helpful to remember that the <command + role="hg-cmd">hg revert</command> command is useful for + changes that you have not yet committed. Once you've committed + a change, if you decide it was a mistake, you can still do + something about it, though your options may be more + limited.</para> + + <para id="x_1e5">For more information about the <command + role="hg-cmd">hg revert</command> command, and details about + how to deal with changes you have already committed, see <xref + linkend="chap:undo"/>.</para> + </sect1> + + <sect1> + <title>Dealing with tricky merges</title> + + <para id="x_687">In a complicated or large project, it's not unusual for a + merge of two changesets to result in some headaches. Suppose + there's a big source file that's been extensively edited by each + side of a merge: this is almost inevitably going to result in + conflicts, some of which can take a few tries to sort + out.</para> + + <para id="x_688">Let's develop a simple case of this and see how to deal with + it. We'll start off with a repository containing one file, and + clone it twice.</para> + + &interaction.ch04-resolve.init; + + <para id="x_689">In one clone, we'll modify the file in one way.</para> + + &interaction.ch04-resolve.left; + + <para id="x_68a">In another, we'll modify the file differently.</para> + + &interaction.ch04-resolve.right; + + <para id="x_68b">Next, we'll pull each set of changes into our original + repo.</para> + + &interaction.ch04-resolve.pull; + + <para id="x_68c">We expect our repository to now contain two heads.</para> + + &interaction.ch04-resolve.heads; + + <para id="x_68d">Normally, if we run <command role="hg-cmd">hg + merge</command> at this point, it will drop us into a GUI that + will let us manually resolve the conflicting edits to + <filename>myfile.txt</filename>. However, to simplify things + for presentation here, we'd like the merge to fail immediately + instead. Here's one way we can do so.</para> + + &interaction.ch04-resolve.export; + + <para id="x_68e">We've told Mercurial's merge machinery to run the command + <command>false</command> (which, as we desire, fails + immediately) if it detects a merge that it can't sort out + automatically.</para> + + <para id="x_68f">If we now fire up <command role="hg-cmd">hg + merge</command>, it should grind to a halt and report a + failure.</para> + + &interaction.ch04-resolve.merge; + + <para id="x_690">Even if we don't notice that the merge failed, Mercurial + will prevent us from accidentally committing the result of a + failed merge.</para> + + &interaction.ch04-resolve.cifail; + + <para id="x_691">When <command role="hg-cmd">hg commit</command> fails in + this case, it suggests that we use the unfamiliar <command + role="hg-cmd">hg resolve</command> command. As usual, + <command role="hg-cmd">hg help resolve</command> will print a + helpful synopsis.</para> + + <sect2> + <title>File resolution states</title> + + <para id="x_692">When a merge occurs, most files will usually remain + unmodified. For each file where Mercurial has to do + something, it tracks the state of the file.</para> + + <itemizedlist> + <listitem> + <para id="x_693">A <emphasis>resolved</emphasis> file has been + successfully merged, either automatically by Mercurial or + manually with human intervention.</para> + </listitem> + <listitem> + <para id="x_694">An <emphasis>unresolved</emphasis> file was not merged + successfully, and needs more attention.</para> + </listitem> + </itemizedlist> + + <para id="x_695">If Mercurial sees <emphasis>any</emphasis> file in the + unresolved state after a merge, it considers the merge to have + failed. Fortunately, we do not need to restart the entire + merge from scratch.</para> + + <para id="x_696">The <option role="hg-opt-resolve">--list</option> or + <option role="hg-opt-resolve">-l</option> option to <command + role="hg-cmd">hg resolve</command> prints out the state of + each merged file.</para> + + &interaction.ch04-resolve.list; + + <para id="x_697">In the output from <command role="hg-cmd">hg + resolve</command>, a resolved file is marked with + <literal>R</literal>, while an unresolved file is marked with + <literal>U</literal>. If any files are listed with + <literal>U</literal>, we know that an attempt to commit the + results of the merge will fail.</para> + </sect2> + + <sect2> + <title>Resolving a file merge</title> + + <para id="x_698">We have several options to move a file from the unresolved + into the resolved state. By far the most common is to rerun + <command role="hg-cmd">hg resolve</command>. If we pass the + names of individual files or directories, it will retry the + merges of any unresolved files present in those locations. We + can also pass the <option role="hg-opt-resolve">--all</option> + or <option role="hg-opt-resolve">-a</option> option, which + will retry the merges of <emphasis>all</emphasis> unresolved + files.</para> + + <para id="x_699">Mercurial also lets us modify the resolution state of a + file directly. We can manually mark a file as resolved using + the <option role="hg-opt-resolve">--mark</option> option, or + as unresolved using the <option + role="hg-opt-resolve">--unmark</option> option. This allows + us to clean up a particularly messy merge by hand, and to keep + track of our progress with each file as we go.</para> + </sect2> + </sect1> + + <sect1> + <title>More useful diffs</title> + + <para id="x_6c7">The default output of the <command role="hg-cmd">hg + diff</command> command is backwards compatible with the + regular <command>diff</command> command, but this has some + drawbacks.</para> + + <para id="x_6c8">Consider the case where we use <command role="hg-cmd">hg + rename</command> to rename a file.</para> + + &interaction.ch04-diff.rename.basic; + + <para id="x_6c9">The output of <command role="hg-cmd">hg diff</command> above + obscures the fact that we simply renamed a file. The <command + role="hg-cmd">hg diff</command> command accepts an option, + <option>--git</option> or <option>-g</option>, to use a newer + diff format that displays such information in a more readable + form.</para> + + &interaction.ch04-diff.rename.git; + + <para id="x_6ca">This option also helps with a case that can otherwise be + confusing: a file that appears to be modified according to + <command role="hg-cmd">hg status</command>, but for which + <command role="hg-cmd">hg diff</command> prints nothing. This + situation can arise if we change the file's execute + permissions.</para> + + &interaction.ch04-diff.chmod; + + <para id="x_6cb">The normal <command>diff</command> command pays no attention + to file permissions, which is why <command role="hg-cmd">hg + diff</command> prints nothing by default. If we supply it + with the <option>-g</option> option, it tells us what really + happened.</para> + + &interaction.ch04-diff.chmod.git; + </sect1> + + <sect1> + <title>Which files to manage, and which to avoid</title> + + <para id="x_6cc">Revision control systems are generally best at managing text + files that are written by humans, such as source code, where the + files do not change much from one revision to the next. Some + centralized revision control systems can also deal tolerably + well with binary files, such as bitmap images.</para> + + <para id="x_6cd">For instance, a game development team will typically manage + both its source code and all of its binary assets (e.g. geometry + data, textures, map layouts) in a revision control + system.</para> + + <para id="x_6ce">Because it is usually impossible to merge two conflicting + modifications to a binary file, centralized systems often + provide a file locking mechanism that allow a user to say + <quote>I am the only person who can edit this + file</quote>.</para> + + <para id="x_6cf">Compared to a centralized system, a distributed revision + control system changes some of the factors that guide decisions + over which files to manage and how.</para> + + <para id="x_6d0">For instance, a distributed revision control system cannot, + by its nature, offer a file locking facility. There is thus no + built-in mechanism to prevent two people from making conflicting + changes to a binary file. If you have a team where several + people may be editing binary files frequently, it may not be a + good idea to use Mercurial&emdash;or any other distributed + revision control system&emdash;to manage those files.</para> + + <para id="x_6d1">When storing modifications to a file, Mercurial usually + saves only the differences between the previous and current + versions of the file. For most text files, this is extremely + efficient. However, some files (particularly binary files) are + laid out in such a way that even a small change to a file's + logical content results in many or most of the bytes inside the + file changing. For instance, compressed files are particularly + susceptible to this. If the differences between each successive + version of a file are always large, Mercurial will not be able + to store the file's revision history very efficiently. This can + affect both local storage needs and the amount of time it takes + to clone a repository.</para> + + <para id="x_6d2">To get an idea of how this could affect you in practice, + suppose you want to use Mercurial to manage an OpenOffice + document. OpenOffice stores documents on disk as compressed zip + files. Edit even a single letter of your document in OpenOffice, + and almost every byte in the entire file will change when you + save it. Now suppose that file is 2MB in size. Because most of + the file changes every time you save, Mercurial will have to + store all 2MB of the file every time you commit, even though + from your perspective, perhaps only a few words are changing + each time. A single frequently-edited file that is not friendly + to Mercurial's storage assumptions can easily have an outsized + effect on the size of the repository.</para> + + <para id="x_6d3">Even worse, if both you and someone else edit the OpenOffice + document you're working on, there is no useful way to merge your + work. In fact, there isn't even a good way to tell what the + differences are between your respective changes.</para> + + <para id="x_6d4">There are thus a few clear recommendations about specific + kinds of files to be very careful with.</para> + + <itemizedlist> + <listitem> + <para id="x_6d5">Files that are very large and incompressible, e.g. ISO + CD-ROM images, will by virtue of sheer size make clones over + a network very slow.</para> + </listitem> + <listitem> + <para id="x_6d6">Files that change a lot from one revision to the next + may be expensive to store if you edit them frequently, and + conflicts due to concurrent edits may be difficult to + resolve.</para> + </listitem> + </itemizedlist> + </sect1> + + <sect1> + <title>Backups and mirroring</title> + + <para id="x_6d7">Since Mercurial maintains a complete copy of history in each + clone, everyone who uses Mercurial to collaborate on a project + can potentially act as a source of backups in the event of a + catastrophe. If a central repository becomes unavailable, you + can construct a replacement simply by cloning a copy of the + repository from one contributor, and pulling any changes they + may not have seen from others.</para> + + <para id="x_6d8">It is simple to use Mercurial to perform off-site backups + and remote mirrors. Set up a periodic job (e.g. via the + <command>cron</command> command) on a remote server to pull + changes from your master repositories every hour. This will + only be tricky in the unlikely case that the number of master + repositories you maintain changes frequently, in which case + you'll need to do a little scripting to refresh the list of + repositories to back up.</para> + + <para id="x_6d9">If you perform traditional backups of your master + repositories to tape or disk, and you want to back up a + repository named <filename>myrepo</filename>, use <command>hg + clone -U myrepo myrepo.bak</command> to create a + clone of <filename>myrepo</filename> before you start your + backups. The <option>-U</option> option doesn't check out a + working directory after the clone completes, since that would be + superfluous and make the backup take longer.</para> + + <para id="x_6da">If you then back up <filename>myrepo.bak</filename> instead + of <filename>myrepo</filename>, you will be guaranteed to have a + consistent snapshot of your repository that won't be pushed to + by an insomniac developer in mid-backup.</para> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch06-collab.xml --- a/fr/ch06-collab.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch06-collab.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,1405 +1,1565 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Collaborating with other people</title> -<para>\label{cha:collab}</para> - -<para>As a completely decentralised tool, Mercurial doesn't impose any -policy on how people ought to work with each other. However, if -you're new to distributed revision control, it helps to have some -tools and examples in mind when you're thinking about possible -workflow models.</para> - -<sect1> -<title>Mercurial's web interface</title> - -<para>Mercurial has a powerful web interface that provides several -useful capabilities.</para> - -<para>For interactive use, the web interface lets you browse a single -repository or a collection of repositories. You can view the history -of a repository, examine each change (comments and diffs), and view -the contents of each directory and file.</para> - -<para>Also for human consumption, the web interface provides an RSS feed of -the changes in a repository. This lets you <quote>subscribe</quote> to a -repository using your favourite feed reader, and be automatically -notified of activity in that repository as soon as it happens. I find -this capability much more convenient than the model of subscribing to -a mailing list to which notifications are sent, as it requires no -additional configuration on the part of whoever is serving the -repository.</para> - -<para>The web interface also lets remote users clone a repository, pull -changes from it, and (when the server is configured to permit it) push -changes back to it. Mercurial's HTTP tunneling protocol aggressively -compresses data, so that it works efficiently even over low-bandwidth -network connections.</para> - -<para>The easiest way to get started with the web interface is to use your -web browser to visit an existing repository, such as the master -Mercurial repository at -<ulink url="http://www.selenic.com/repo/hg?style=gitweb">http://www.selenic.com/repo/hg?style=gitweb</ulink>.</para> - -<para>If you're interested in providing a web interface to your own -repositories, Mercurial provides two ways to do this. The first is -using the <command role="hg-cmd">hg serve</command> command, which is best suited to short-term -<quote>lightweight</quote> serving. See section <xref linkend="sec:collab:serve"/> below for -details of how to use this command. If you have a long-lived -repository that you'd like to make permanently available, Mercurial -has built-in support for the CGI (Common Gateway Interface) standard, -which all common web servers support. See -section <xref linkend="sec:collab:cgi"/> for details of CGI configuration.</para> - -</sect1> -<sect1> -<title>Collaboration models</title> - -<para>With a suitably flexible tool, making decisions about workflow is much -more of a social engineering challenge than a technical one. -Mercurial imposes few limitations on how you can structure the flow of -work in a project, so it's up to you and your group to set up and live -with a model that matches your own particular needs. -</para> - -<sect2> -<title>Factors to keep in mind</title> - -<para>The most important aspect of any model that you must keep in mind is -how well it matches the needs and capabilities of the people who will -be using it. This might seem self-evident; even so, you still can't -afford to forget it for a moment. -</para> - -<para>I once put together a workflow model that seemed to make perfect sense -to me, but that caused a considerable amount of consternation and -strife within my development team. In spite of my attempts to explain -why we needed a complex set of branches, and how changes ought to flow -between them, a few team members revolted. Even though they were -smart people, they didn't want to pay attention to the constraints we -were operating under, or face the consequences of those constraints in -the details of the model that I was advocating. -</para> - -<para>Don't sweep foreseeable social or technical problems under the rug. -Whatever scheme you put into effect, you should plan for mistakes and -problem scenarios. Consider adding automated machinery to prevent, or -quickly recover from, trouble that you can anticipate. As an example, -if you intend to have a branch with not-for-release changes in it, -you'd do well to think early about the possibility that someone might -accidentally merge those changes into a release branch. You could -avoid this particular problem by writing a hook that prevents changes -from being merged from an inappropriate branch. -</para> - -</sect2> -<sect2> -<title>Informal anarchy</title> - -<para>I wouldn't suggest an <quote>anything goes</quote> approach as something -sustainable, but it's a model that's easy to grasp, and it works -perfectly well in a few unusual situations. -</para> - -<para>As one example, many projects have a loose-knit group of collaborators -who rarely physically meet each other. Some groups like to overcome -the isolation of working at a distance by organising occasional -<quote>sprints</quote>. In a sprint, a number of people get together in a single -location (a company's conference room, a hotel meeting room, that kind -of place) and spend several days more or less locked in there, hacking -intensely on a handful of projects. -</para> - -<para>A sprint is the perfect place to use the <command role="hg-cmd">hg serve</command> command, since -<command role="hg-cmd">hg serve</command> does not requires any fancy server infrastructure. You -can get started with <command role="hg-cmd">hg serve</command> in moments, by reading -section <xref linkend="sec:collab:serve"/> below. Then simply tell the person -next to you that you're running a server, send the URL to them in an -instant message, and you immediately have a quick-turnaround way to -work together. They can type your URL into their web browser and -quickly review your changes; or they can pull a bugfix from you and -verify it; or they can clone a branch containing a new feature and try -it out. -</para> - -<para>The charm, and the problem, with doing things in an ad hoc fashion -like this is that only people who know about your changes, and where -they are, can see them. Such an informal approach simply doesn't -scale beyond a handful people, because each individual needs to know -about $n$ different repositories to pull from. -</para> - -</sect2> -<sect2> -<title>A single central repository</title> - -<para>For smaller projects migrating from a centralised revision control -tool, perhaps the easiest way to get started is to have changes flow -through a single shared central repository. This is also the -most common <quote>building block</quote> for more ambitious workflow schemes. -</para> - -<para>Contributors start by cloning a copy of this repository. They can -pull changes from it whenever they need to, and some (perhaps all) -developers have permission to push a change back when they're ready -for other people to see it. -</para> - -<para>Under this model, it can still often make sense for people to pull -changes directly from each other, without going through the central -repository. Consider a case in which I have a tentative bug fix, but -I am worried that if I were to publish it to the central repository, -it might subsequently break everyone else's trees as they pull it. To -reduce the potential for damage, I can ask you to clone my repository -into a temporary repository of your own and test it. This lets us put -off publishing the potentially unsafe change until it has had a little -testing. -</para> - -<para>In this kind of scenario, people usually use the <command>ssh</command> -protocol to securely push changes to the central repository, as -documented in section <xref linkend="sec:collab:ssh"/>. It's also usual to -publish a read-only copy of the repository over HTTP using CGI, as in -section <xref linkend="sec:collab:cgi"/>. Publishing over HTTP satisfies the -needs of people who don't have push access, and those who want to use -web browsers to browse the repository's history. -</para> - -</sect2> -<sect2> -<title>Working with multiple branches</title> - -<para>Projects of any significant size naturally tend to make progress on -several fronts simultaneously. In the case of software, it's common -for a project to go through periodic official releases. A release -might then go into <quote>maintenance mode</quote> for a while after its first -publication; maintenance releases tend to contain only bug fixes, not -new features. In parallel with these maintenance releases, one or -more future releases may be under development. People normally use -the word <quote>branch</quote> to refer to one of these many slightly different -directions in which development is proceeding. -</para> - -<para>Mercurial is particularly well suited to managing a number of -simultaneous, but not identical, branches. Each <quote>development -direction</quote> can live in its own central repository, and you can merge -changes from one to another as the need arises. Because repositories -are independent of each other, unstable changes in a development -branch will never affect a stable branch unless someone explicitly -merges those changes in. -</para> - -<para>Here's an example of how this can work in practice. Let's say you -have one <quote>main branch</quote> on a central server. -<!-- &interaction.branching.init; --> -People clone it, make changes locally, test them, and push them back. -</para> - -<para>Once the main branch reaches a release milestone, you can use the -<command role="hg-cmd">hg tag</command> command to give a permanent name to the milestone -revision. -<!-- &interaction.branching.tag; --> -Let's say some ongoing development occurs on the main branch. -<!-- &interaction.branching.main; --> -Using the tag that was recorded at the milestone, people who clone -that repository at any time in the future can use <command role="hg-cmd">hg update</command> to -get a copy of the working directory exactly as it was when that tagged -revision was committed. -<!-- &interaction.branching.update; --> -</para> - -<para>In addition, immediately after the main branch is tagged, someone can -then clone the main branch on the server to a new <quote>stable</quote> branch, -also on the server. -<!-- &interaction.branching.clone; --> -</para> - -<para>Someone who needs to make a change to the stable branch can then clone -<emphasis>that</emphasis> repository, make their changes, commit, and push their -changes back there. -<!-- &interaction.branching.stable; --> -Because Mercurial repositories are independent, and Mercurial doesn't -move changes around automatically, the stable and main branches are -<emphasis>isolated</emphasis> from each other. The changes that you made on the -main branch don't <quote>leak</quote> to the stable branch, and vice versa. -</para> - -<para>You'll often want all of your bugfixes on the stable branch to show up -on the main branch, too. Rather than rewrite a bugfix on the main -branch, you can simply pull and merge changes from the stable to the -main branch, and Mercurial will bring those bugfixes in for you. -<!-- &interaction.branching.merge; --> -The main branch will still contain changes that are not on the stable -branch, but it will also contain all of the bugfixes from the stable -branch. The stable branch remains unaffected by these changes. -</para> - -</sect2> -<sect2> -<title>Feature branches</title> - -<para>For larger projects, an effective way to manage change is to break up -a team into smaller groups. Each group has a shared branch of its -own, cloned from a single <quote>master</quote> branch used by the entire -project. People working on an individual branch are typically quite -isolated from developments on other branches. -</para> - -<informalfigure> - -<para> <mediaobject><imageobject><imagedata fileref="feature-branches"/></imageobject><textobject><phrase>XXX add text</phrase></textobject></mediaobject> - <caption><para>Feature branches</para></caption> - \label{fig:collab:feature-branches} -</para> -</informalfigure> - -<para>When a particular feature is deemed to be in suitable shape, someone -on that feature team pulls and merges from the master branch into the -feature branch, then pushes back up to the master branch. -</para> - -</sect2> -<sect2> -<title>The release train</title> - -<para>Some projects are organised on a <quote>train</quote> basis: a release is -scheduled to happen every few months, and whatever features are ready -when the <quote>train</quote> is ready to leave are allowed in. -</para> - -<para>This model resembles working with feature branches. The difference is -that when a feature branch misses a train, someone on the feature team -pulls and merges the changes that went out on that train release into -the feature branch, and the team continues its work on top of that -release so that their feature can make the next release. -</para> - -</sect2> -<sect2> -<title>The Linux kernel model</title> - -<para>The development of the Linux kernel has a shallow hierarchical -structure, surrounded by a cloud of apparent chaos. Because most -Linux developers use <command>git</command>, a distributed revision control -tool with capabilities similar to Mercurial, it's useful to describe -the way work flows in that environment; if you like the ideas, the -approach translates well across tools. -</para> - -<para>At the center of the community sits Linus Torvalds, the creator of -Linux. He publishes a single source repository that is considered the -<quote>authoritative</quote> current tree by the entire developer community. -Anyone can clone Linus's tree, but he is very choosy about whose trees -he pulls from. -</para> - -<para>Linus has a number of <quote>trusted lieutenants</quote>. As a general rule, he -pulls whatever changes they publish, in most cases without even -reviewing those changes. Some of those lieutenants are generally -agreed to be <quote>maintainers</quote>, responsible for specific subsystems -within the kernel. If a random kernel hacker wants to make a change -to a subsystem that they want to end up in Linus's tree, they must -find out who the subsystem's maintainer is, and ask that maintainer to -take their change. If the maintainer reviews their changes and agrees -to take them, they'll pass them along to Linus in due course. -</para> - -<para>Individual lieutenants have their own approaches to reviewing, -accepting, and publishing changes; and for deciding when to feed them -to Linus. In addition, there are several well known branches that -people use for different purposes. For example, a few people maintain -<quote>stable</quote> repositories of older versions of the kernel, to which they -apply critical fixes as needed. Some maintainers publish multiple -trees: one for experimental changes; one for changes that they are -about to feed upstream; and so on. Others just publish a single -tree. -</para> - -<para>This model has two notable features. The first is that it's <quote>pull -only</quote>. You have to ask, convince, or beg another developer to take a -change from you, because there are almost no trees to which more than -one person can push, and there's no way to push changes into a tree -that someone else controls. -</para> - -<para>The second is that it's based on reputation and acclaim. If you're an -unknown, Linus will probably ignore changes from you without even -responding. But a subsystem maintainer will probably review them, and -will likely take them if they pass their criteria for suitability. -The more <quote>good</quote> changes you contribute to a maintainer, the more -likely they are to trust your judgment and accept your changes. If -you're well-known and maintain a long-lived branch for something Linus -hasn't yet accepted, people with similar interests may pull your -changes regularly to keep up with your work. -</para> - -<para>Reputation and acclaim don't necessarily cross subsystem or <quote>people</quote> -boundaries. If you're a respected but specialised storage hacker, and -you try to fix a networking bug, that change will receive a level of -scrutiny from a network maintainer comparable to a change from a -complete stranger. -</para> - -<para>To people who come from more orderly project backgrounds, the -comparatively chaotic Linux kernel development process often seems -completely insane. It's subject to the whims of individuals; people -make sweeping changes whenever they deem it appropriate; and the pace -of development is astounding. And yet Linux is a highly successful, -well-regarded piece of software. -</para> - -</sect2> -<sect2> -<title>Pull-only versus shared-push collaboration</title> - -<para>A perpetual source of heat in the open source community is whether a -development model in which people only ever pull changes from others -is <quote>better than</quote> one in which multiple people can push changes to a -shared repository. -</para> - -<para>Typically, the backers of the shared-push model use tools that -actively enforce this approach. If you're using a centralised -revision control tool such as Subversion, there's no way to make a -choice over which model you'll use: the tool gives you shared-push, -and if you want to do anything else, you'll have to roll your own -approach on top (such as applying a patch by hand). -</para> - -<para>A good distributed revision control tool, such as Mercurial, will -support both models. You and your collaborators can then structure -how you work together based on your own needs and preferences, not on -what contortions your tools force you into. -</para> - -</sect2> -<sect2> -<title>Where collaboration meets branch management</title> - -<para>Once you and your team set up some shared repositories and start -propagating changes back and forth between local and shared repos, you -begin to face a related, but slightly different challenge: that of -managing the multiple directions in which your team may be moving at -once. Even though this subject is intimately related to how your team -collaborates, it's dense enough to merit treatment of its own, in -chapter <xref linkend="chap:branch"/>. -</para> - -</sect2> -</sect1> -<sect1> -<title>The technical side of sharing</title> - -<para>The remainder of this chapter is devoted to the question of serving -data to your collaborators. -</para> - -</sect1> -<sect1> -<title>Informal sharing with <command role="hg-cmd">hg serve</command></title> -<para>\label{sec:collab:serve} -</para> - -<para>Mercurial's <command role="hg-cmd">hg serve</command> command is wonderfully suited to small, -tight-knit, and fast-paced group environments. It also provides a -great way to get a feel for using Mercurial commands over a network. -</para> - -<para>Run <command role="hg-cmd">hg serve</command> inside a repository, and in under a second it will -bring up a specialised HTTP server; this will accept connections from -any client, and serve up data for that repository until you terminate -it. Anyone who knows the URL of the server you just started, and can -talk to your computer over the network, can then use a web browser or -Mercurial to read data from that repository. A URL for a -<command role="hg-cmd">hg serve</command> instance running on a laptop is likely to look something -like <literal>http://my-laptop.local:8000/</literal>. -</para> - -<para>The <command role="hg-cmd">hg serve</command> command is <emphasis>not</emphasis> a general-purpose web server. -It can do only two things: -</para> -<itemizedlist> -<listitem><para>Allow people to browse the history of the repository it's - serving, from their normal web browsers. -</para> -</listitem> -<listitem><para>Speak Mercurial's wire protocol, so that people can - <command role="hg-cmd">hg clone</command> or <command role="hg-cmd">hg pull</command> changes from that repository. -</para> -</listitem></itemizedlist> -<para>In particular, <command role="hg-cmd">hg serve</command> won't allow remote users to <emphasis>modify</emphasis> -your repository. It's intended for read-only use. -</para> - -<para>If you're getting started with Mercurial, there's nothing to prevent -you from using <command role="hg-cmd">hg serve</command> to serve up a repository on your own -computer, then use commands like <command role="hg-cmd">hg clone</command>, <command role="hg-cmd">hg incoming</command>, and -so on to talk to that server as if the repository was hosted remotely. -This can help you to quickly get acquainted with using commands on -network-hosted repositories. -</para> - -<sect2> -<title>A few things to keep in mind</title> - -<para>Because it provides unauthenticated read access to all clients, you -should only use <command role="hg-cmd">hg serve</command> in an environment where you either don't -care, or have complete control over, who can access your network and -pull data from your repository. -</para> - -<para>The <command role="hg-cmd">hg serve</command> command knows nothing about any firewall software -you might have installed on your system or network. It cannot detect -or control your firewall software. If other people are unable to talk -to a running <command role="hg-cmd">hg serve</command> instance, the second thing you should do -(<emphasis>after</emphasis> you make sure that they're using the correct URL) is -check your firewall configuration. -</para> - -<para>By default, <command role="hg-cmd">hg serve</command> listens for incoming connections on -port 8000. If another process is already listening on the port you -want to use, you can specify a different port to listen on using the -<option role="hg-opt-serve">-p</option> option. -</para> - -<para>Normally, when <command role="hg-cmd">hg serve</command> starts, it prints no output, which can be -a bit unnerving. If you'd like to confirm that it is indeed running -correctly, and find out what URL you should send to your -collaborators, start it with the <option role="hg-opt-global">-v</option> option. -</para> - -</sect2> -</sect1> -<sect1> -<title>Using the Secure Shell (ssh) protocol</title> -<para>\label{sec:collab:ssh} -</para> - -<para>You can pull and push changes securely over a network connection using -the Secure Shell (<literal>ssh</literal>) protocol. To use this successfully, -you may have to do a little bit of configuration on the client or -server sides. -</para> - -<para>If you're not familiar with ssh, it's a network protocol that lets you -securely communicate with another computer. To use it with Mercurial, -you'll be setting up one or more user accounts on a server so that -remote users can log in and execute commands. -</para> - -<para>(If you <emphasis>are</emphasis> familiar with ssh, you'll probably find some of the -material that follows to be elementary in nature.) -</para> - -<sect2> -<title>How to read and write ssh URLs</title> - -<para>An ssh URL tends to look like this: -</para> -<programlisting> -<para> ssh://bos@hg.serpentine.com:22/hg/hgbook -</para> +<chapter id="cha:collab"> + <?dbhtml filename="collaborating-with-other-people.html"?> + <title>Collaborating with other people</title> + + <para id="x_44a">As a completely decentralised tool, Mercurial doesn't impose + any policy on how people ought to work with each other. However, + if you're new to distributed revision control, it helps to have + some tools and examples in mind when you're thinking about + possible workflow models.</para> + + <sect1> + <title>Mercurial's web interface</title> + + <para id="x_44b">Mercurial has a powerful web interface that provides several + useful capabilities.</para> + + <para id="x_44c">For interactive use, the web interface lets you browse a + single repository or a collection of repositories. You can view + the history of a repository, examine each change (comments and + diffs), and view the contents of each directory and file. You + can even get a view of history that gives a graphical view of + the relationships between individual changes and merges.</para> + + <para id="x_44d">Also for human consumption, the web interface provides + Atom and RSS feeds of the changes in a repository. This lets you + <quote>subscribe</quote> to a repository using your favorite + feed reader, and be automatically notified of activity in that + repository as soon as it happens. I find this capability much + more convenient than the model of subscribing to a mailing list + to which notifications are sent, as it requires no additional + configuration on the part of whoever is serving the + repository.</para> + + <para id="x_44e">The web interface also lets remote users clone a repository, + pull changes from it, and (when the server is configured to + permit it) push changes back to it. Mercurial's HTTP tunneling + protocol aggressively compresses data, so that it works + efficiently even over low-bandwidth network connections.</para> + + <para id="x_44f">The easiest way to get started with the web interface is to + use your web browser to visit an existing repository, such as + the master Mercurial repository at <ulink + url="http://www.selenic.com/repo/hg">http://www.selenic.com/repo/hg</ulink>.</para> + + <para id="x_450">If you're interested in providing a web interface + to your own repositories, there are several good ways to do + this.</para> + + <para id="x_69d">The easiest and fastest way to get started in an informal + environment is to use the <command role="hg-cmd">hg + serve</command> command, which is best suited to short-term + <quote>lightweight</quote> serving. See <xref + linkend="sec:collab:serve"/> below for details of how to use + this command.</para> + + <para id="x_69e">For longer-lived repositories that you'd like to + have permanently available, there are several public hosting + services available. Some are free to open source projects, + while others offer paid commercial hosting. An up-to-date list + is available at <ulink + url="http://www.selenic.com/mercurial/wiki/index.cgi/MercurialHosting">http://www.selenic.com/mercurial/wiki/index.cgi/MercurialHosting</ulink>.</para> + + <para id="x_6a0">If you would prefer to host your own repositories, Mercurial + has built-in support for several popular hosting technologies, + most notably CGI (Common Gateway Interface), and WSGI (Web + Services Gateway Interface). See <xref + linkend="sec:collab:cgi"/> for details of CGI and WSGI + configuration.</para> + </sect1> + + <sect1> + <title>Collaboration models</title> + + <para id="x_451">With a suitably flexible tool, making decisions about + workflow is much more of a social engineering challenge than a + technical one. Mercurial imposes few limitations on how you can + structure the flow of work in a project, so it's up to you and + your group to set up and live with a model that matches your own + particular needs.</para> + + <sect2> + <title>Factors to keep in mind</title> + + <para id="x_452">The most important aspect of any model that you must keep + in mind is how well it matches the needs and capabilities of + the people who will be using it. This might seem + self-evident; even so, you still can't afford to forget it for + a moment.</para> + + <para id="x_453">I once put together a workflow model that seemed to make + perfect sense to me, but that caused a considerable amount of + consternation and strife within my development team. In spite + of my attempts to explain why we needed a complex set of + branches, and how changes ought to flow between them, a few + team members revolted. Even though they were smart people, + they didn't want to pay attention to the constraints we were + operating under, or face the consequences of those constraints + in the details of the model that I was advocating.</para> + + <para id="x_454">Don't sweep foreseeable social or technical problems under + the rug. Whatever scheme you put into effect, you should plan + for mistakes and problem scenarios. Consider adding automated + machinery to prevent, or quickly recover from, trouble that + you can anticipate. As an example, if you intend to have a + branch with not-for-release changes in it, you'd do well to + think early about the possibility that someone might + accidentally merge those changes into a release branch. You + could avoid this particular problem by writing a hook that + prevents changes from being merged from an inappropriate + branch.</para> + </sect2> + + <sect2> + <title>Informal anarchy</title> + + <para id="x_455">I wouldn't suggest an <quote>anything goes</quote> + approach as something sustainable, but it's a model that's + easy to grasp, and it works perfectly well in a few unusual + situations.</para> + + <para id="x_456">As one example, many projects have a loose-knit group of + collaborators who rarely physically meet each other. Some + groups like to overcome the isolation of working at a distance + by organizing occasional <quote>sprints</quote>. In a sprint, + a number of people get together in a single location (a + company's conference room, a hotel meeting room, that kind of + place) and spend several days more or less locked in there, + hacking intensely on a handful of projects.</para> + + <para id="x_457">A sprint or a hacking session in a coffee shop are the perfect places to use the + <command role="hg-cmd">hg serve</command> command, since + <command role="hg-cmd">hg serve</command> does not require any + fancy server infrastructure. You can get started with + <command role="hg-cmd">hg serve</command> in moments, by + reading <xref linkend="sec:collab:serve"/> below. Then simply + tell the person next to you that you're running a server, send + the URL to them in an instant message, and you immediately + have a quick-turnaround way to work together. They can type + your URL into their web browser and quickly review your + changes; or they can pull a bugfix from you and verify it; or + they can clone a branch containing a new feature and try it + out.</para> + + <para id="x_458">The charm, and the problem, with doing things + in an ad hoc fashion like this is that only people who know + about your changes, and where they are, can see them. Such an + informal approach simply doesn't scale beyond a handful + people, because each individual needs to know about + <emphasis>n</emphasis> different repositories to pull + from.</para> + </sect2> + + <sect2> + <title>A single central repository</title> + + <para id="x_459">For smaller projects migrating from a centralised revision + control tool, perhaps the easiest way to get started is to + have changes flow through a single shared central repository. + This is also the most common <quote>building block</quote> for + more ambitious workflow schemes.</para> + + <para id="x_45a">Contributors start by cloning a copy of this repository. + They can pull changes from it whenever they need to, and some + (perhaps all) developers have permission to push a change back + when they're ready for other people to see it.</para> + + <para id="x_45b">Under this model, it can still often make sense for people + to pull changes directly from each other, without going + through the central repository. Consider a case in which I + have a tentative bug fix, but I am worried that if I were to + publish it to the central repository, it might subsequently + break everyone else's trees as they pull it. To reduce the + potential for damage, I can ask you to clone my repository + into a temporary repository of your own and test it. This + lets us put off publishing the potentially unsafe change until + it has had a little testing.</para> + + <para id="x_45c">If a team is hosting its own repository in this + kind of scenario, people will usually use the + <command>ssh</command> protocol to securely push changes to + the central repository, as documented in <xref + linkend="sec:collab:ssh"/>. It's also usual to publish a + read-only copy of the repository over HTTP, as in + <xref linkend="sec:collab:cgi"/>. Publishing over HTTP + satisfies the needs of people who don't have push access, and + those who want to use web browsers to browse the repository's + history.</para> + </sect2> + + <sect2> + <title>A hosted central repository</title> + + <para id="x_6a1">A wonderful thing about public hosting services like + <ulink url="http://bitbucket.org/">Bitbucket</ulink> is that + not only do they handle the fiddly server configuration + details, such as user accounts, authentication, and secure + wire protocols, they provide additional infrastructure to make + this model work well.</para> + + <para id="x_6a2">For instance, a well-engineered hosting service will let + people clone their own copies of a repository with a single + click. This lets people work in separate spaces and share + their changes when they're ready.</para> + + <para id="x_6a3">In addition, a good hosting service will let people + communicate with each other, for instance to say <quote>there + are changes ready for you to review in this + tree</quote>.</para> + </sect2> + + <sect2> + <title>Working with multiple branches</title> + + <para id="x_45d">Projects of any significant size naturally tend to make + progress on several fronts simultaneously. In the case of + software, it's common for a project to go through periodic + official releases. A release might then go into + <quote>maintenance mode</quote> for a while after its first + publication; maintenance releases tend to contain only bug + fixes, not new features. In parallel with these maintenance + releases, one or more future releases may be under + development. People normally use the word + <quote>branch</quote> to refer to one of these many slightly + different directions in which development is + proceeding.</para> + + <para id="x_45e">Mercurial is particularly well suited to managing a number + of simultaneous, but not identical, branches. Each + <quote>development direction</quote> can live in its own + central repository, and you can merge changes from one to + another as the need arises. Because repositories are + independent of each other, unstable changes in a development + branch will never affect a stable branch unless someone + explicitly merges those changes into the stable branch.</para> + + <para id="x_45f">Here's an example of how this can work in practice. Let's + say you have one <quote>main branch</quote> on a central + server.</para> + + &interaction.branching.init; + + <para id="x_460">People clone it, make changes locally, test them, and push + them back.</para> + + <para id="x_461">Once the main branch reaches a release milestone, you can + use the <command role="hg-cmd">hg tag</command> command to + give a permanent name to the milestone revision.</para> + + &interaction.branching.tag; + + <para id="x_462">Let's say some ongoing + development occurs on the main branch.</para> + + &interaction.branching.main; + + <para id="x_463">Using the tag that was recorded at the milestone, people + who clone that repository at any time in the future can use + <command role="hg-cmd">hg update</command> to get a copy of + the working directory exactly as it was when that tagged + revision was committed.</para> + + &interaction.branching.update; + + <para id="x_464">In addition, immediately after the main branch is tagged, + we can then clone the main branch on the server to a new + <quote>stable</quote> branch, also on the server.</para> + + &interaction.branching.clone; + + <para id="x_465">If we need to make a change to the stable + branch, we can then clone <emphasis>that</emphasis> + repository, make our changes, commit, and push our changes + back there.</para> + + &interaction.branching.stable; + + <para id="x_466">Because Mercurial repositories are independent, and + Mercurial doesn't move changes around automatically, the + stable and main branches are <emphasis>isolated</emphasis> + from each other. The changes that we made on the main branch + don't <quote>leak</quote> to the stable branch, and vice + versa.</para> + + <para id="x_467">We'll often want all of our bugfixes on the stable + branch to show up on the main branch, too. Rather than + rewrite a bugfix on the main branch, we can simply pull and + merge changes from the stable to the main branch, and + Mercurial will bring those bugfixes in for us.</para> + + &interaction.branching.merge; + + <para id="x_468">The main branch will still contain changes that + are not on the stable branch, but it will also contain all of + the bugfixes from the stable branch. The stable branch + remains unaffected by these changes, since changes are only + flowing from the stable to the main branch, and not the other + way.</para> + </sect2> + + <sect2> + <title>Feature branches</title> + + <para id="x_469">For larger projects, an effective way to manage change is + to break up a team into smaller groups. Each group has a + shared branch of its own, cloned from a single + <quote>master</quote> branch used by the entire project. + People working on an individual branch are typically quite + isolated from developments on other branches.</para> + + <figure id="fig:collab:feature-branches"> + <title>Feature branches</title> + <mediaobject> + <imageobject><imagedata width="100%" fileref="figs/feature-branches.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + + <para id="x_46b">When a particular feature is deemed to be in suitable + shape, someone on that feature team pulls and merges from the + master branch into the feature branch, then pushes back up to + the master branch.</para> + </sect2> + + <sect2> + <title>The release train</title> + + <para id="x_46c">Some projects are organized on a <quote>train</quote> + basis: a release is scheduled to happen every few months, and + whatever features are ready when the <quote>train</quote> is + ready to leave are allowed in.</para> + + <para id="x_46d">This model resembles working with feature branches. The + difference is that when a feature branch misses a train, + someone on the feature team pulls and merges the changes that + went out on that train release into the feature branch, and + the team continues its work on top of that release so that + their feature can make the next release.</para> + </sect2> + + <sect2> + <title>The Linux kernel model</title> + + <para id="x_46e">The development of the Linux kernel has a shallow + hierarchical structure, surrounded by a cloud of apparent + chaos. Because most Linux developers use + <command>git</command>, a distributed revision control tool + with capabilities similar to Mercurial, it's useful to + describe the way work flows in that environment; if you like + the ideas, the approach translates well across tools.</para> + + <para id="x_46f">At the center of the community sits Linus Torvalds, the + creator of Linux. He publishes a single source repository + that is considered the <quote>authoritative</quote> current + tree by the entire developer community. Anyone can clone + Linus's tree, but he is very choosy about whose trees he pulls + from.</para> + + <para id="x_470">Linus has a number of <quote>trusted lieutenants</quote>. + As a general rule, he pulls whatever changes they publish, in + most cases without even reviewing those changes. Some of + those lieutenants are generally agreed to be + <quote>maintainers</quote>, responsible for specific + subsystems within the kernel. If a random kernel hacker wants + to make a change to a subsystem that they want to end up in + Linus's tree, they must find out who the subsystem's + maintainer is, and ask that maintainer to take their change. + If the maintainer reviews their changes and agrees to take + them, they'll pass them along to Linus in due course.</para> + + <para id="x_471">Individual lieutenants have their own approaches to + reviewing, accepting, and publishing changes; and for deciding + when to feed them to Linus. In addition, there are several + well known branches that people use for different purposes. + For example, a few people maintain <quote>stable</quote> + repositories of older versions of the kernel, to which they + apply critical fixes as needed. Some maintainers publish + multiple trees: one for experimental changes; one for changes + that they are about to feed upstream; and so on. Others just + publish a single tree.</para> + + <para id="x_472">This model has two notable features. The first is that + it's <quote>pull only</quote>. You have to ask, convince, or + beg another developer to take a change from you, because there + are almost no trees to which more than one person can push, + and there's no way to push changes into a tree that someone + else controls.</para> + + <para id="x_473">The second is that it's based on reputation and acclaim. + If you're an unknown, Linus will probably ignore changes from + you without even responding. But a subsystem maintainer will + probably review them, and will likely take them if they pass + their criteria for suitability. The more <quote>good</quote> + changes you contribute to a maintainer, the more likely they + are to trust your judgment and accept your changes. If you're + well-known and maintain a long-lived branch for something + Linus hasn't yet accepted, people with similar interests may + pull your changes regularly to keep up with your work.</para> + + <para id="x_474">Reputation and acclaim don't necessarily cross subsystem + or <quote>people</quote> boundaries. If you're a respected + but specialised storage hacker, and you try to fix a + networking bug, that change will receive a level of scrutiny + from a network maintainer comparable to a change from a + complete stranger.</para> + + <para id="x_475">To people who come from more orderly project backgrounds, + the comparatively chaotic Linux kernel development process + often seems completely insane. It's subject to the whims of + individuals; people make sweeping changes whenever they deem + it appropriate; and the pace of development is astounding. + And yet Linux is a highly successful, well-regarded piece of + software.</para> + </sect2> + + <sect2> + <title>Pull-only versus shared-push collaboration</title> + + <para id="x_476">A perpetual source of heat in the open source community is + whether a development model in which people only ever pull + changes from others is <quote>better than</quote> one in which + multiple people can push changes to a shared + repository.</para> + + <para id="x_477">Typically, the backers of the shared-push model use tools + that actively enforce this approach. If you're using a + centralised revision control tool such as Subversion, there's + no way to make a choice over which model you'll use: the tool + gives you shared-push, and if you want to do anything else, + you'll have to roll your own approach on top (such as applying + a patch by hand).</para> + + <para id="x_478">A good distributed revision control tool will + support both models. You and your collaborators can then + structure how you work together based on your own needs and + preferences, not on what contortions your tools force you + into.</para> + </sect2> + <sect2> + <title>Where collaboration meets branch management</title> + + <para id="x_479">Once you and your team set up some shared + repositories and start propagating changes back and forth + between local and shared repos, you begin to face a related, + but slightly different challenge: that of managing the + multiple directions in which your team may be moving at once. + Even though this subject is intimately related to how your + team collaborates, it's dense enough to merit treatment of its + own, in <xref linkend="chap:branch"/>.</para> + </sect2> + </sect1> + + <sect1> + <title>The technical side of sharing</title> + + <para id="x_47a">The remainder of this chapter is devoted to the question of + sharing changes with your collaborators.</para> + </sect1> + + <sect1 id="sec:collab:serve"> + <title>Informal sharing with <command role="hg-cmd">hg + serve</command></title> + + <para id="x_47b">Mercurial's <command role="hg-cmd">hg serve</command> + command is wonderfully suited to small, tight-knit, and + fast-paced group environments. It also provides a great way to + get a feel for using Mercurial commands over a network.</para> + + <para id="x_47c">Run <command role="hg-cmd">hg serve</command> inside a + repository, and in under a second it will bring up a specialised + HTTP server; this will accept connections from any client, and + serve up data for that repository until you terminate it. + Anyone who knows the URL of the server you just started, and can + talk to your computer over the network, can then use a web + browser or Mercurial to read data from that repository. A URL + for a <command role="hg-cmd">hg serve</command> instance running + on a laptop is likely to look something like + <literal>http://my-laptop.local:8000/</literal>.</para> + + <para id="x_47d">The <command role="hg-cmd">hg serve</command> command is + <emphasis>not</emphasis> a general-purpose web server. It can do + only two things:</para> + <itemizedlist> + <listitem><para id="x_47e">Allow people to browse the history of the + repository it's serving, from their normal web + browsers.</para> + </listitem> + <listitem><para id="x_47f">Speak Mercurial's wire protocol, so that people + can <command role="hg-cmd">hg clone</command> or <command + role="hg-cmd">hg pull</command> changes from that + repository.</para> + </listitem></itemizedlist> + <para id="x_480">In particular, <command role="hg-cmd">hg serve</command> + won't allow remote users to <emphasis>modify</emphasis> your + repository. It's intended for read-only use.</para> + + <para id="x_481">If you're getting started with Mercurial, there's nothing to + prevent you from using <command role="hg-cmd">hg serve</command> + to serve up a repository on your own computer, then use commands + like <command role="hg-cmd">hg clone</command>, <command + role="hg-cmd">hg incoming</command>, and so on to talk to that + server as if the repository was hosted remotely. This can help + you to quickly get acquainted with using commands on + network-hosted repositories.</para> + + <sect2> + <title>A few things to keep in mind</title> + + <para id="x_482">Because it provides unauthenticated read access to all + clients, you should only use <command role="hg-cmd">hg + serve</command> in an environment where you either don't + care, or have complete control over, who can access your + network and pull data from your repository.</para> + + <para id="x_483">The <command role="hg-cmd">hg serve</command> command + knows nothing about any firewall software you might have + installed on your system or network. It cannot detect or + control your firewall software. If other people are unable to + talk to a running <command role="hg-cmd">hg serve</command> + instance, the second thing you should do + (<emphasis>after</emphasis> you make sure that they're using + the correct URL) is check your firewall configuration.</para> + + <para id="x_484">By default, <command role="hg-cmd">hg serve</command> + listens for incoming connections on port 8000. If another + process is already listening on the port you want to use, you + can specify a different port to listen on using the <option + role="hg-opt-serve">-p</option> option.</para> + + <para id="x_485">Normally, when <command role="hg-cmd">hg serve</command> + starts, it prints no output, which can be a bit unnerving. If + you'd like to confirm that it is indeed running correctly, and + find out what URL you should send to your collaborators, start + it with the <option role="hg-opt-global">-v</option> + option.</para> + </sect2> + </sect1> + + <sect1 id="sec:collab:ssh"> + <title>Using the Secure Shell (ssh) protocol</title> + + <para id="x_486">You can pull and push changes securely over a network + connection using the Secure Shell (<literal>ssh</literal>) + protocol. To use this successfully, you may have to do a little + bit of configuration on the client or server sides.</para> + + <para id="x_487">If you're not familiar with ssh, it's the name of + both a command and a network protocol that let you securely + communicate with another computer. To use it with Mercurial, + you'll be setting up one or more user accounts on a server so + that remote users can log in and execute commands.</para> + + <para id="x_488">(If you <emphasis>are</emphasis> familiar with ssh, you'll + probably find some of the material that follows to be elementary + in nature.)</para> + + <sect2> + <title>How to read and write ssh URLs</title> + + <para id="x_489">An ssh URL tends to look like this:</para> + <programlisting>ssh://bos@hg.serpentine.com:22/hg/hgbook</programlisting> + <orderedlist> + <listitem><para id="x_48a">The <quote><literal>ssh://</literal></quote> + part tells Mercurial to use the ssh protocol.</para> + </listitem> + <listitem><para id="x_48b">The <quote><literal>bos@</literal></quote> + component indicates what username to log into the server + as. You can leave this out if the remote username is the + same as your local username.</para> + </listitem> + <listitem><para id="x_48c">The + <quote><literal>hg.serpentine.com</literal></quote> gives + the hostname of the server to log into.</para> + </listitem> + <listitem><para id="x_48d">The <quote>:22</quote> identifies the port + number to connect to the server on. The default port is + 22, so you only need to specify a colon and port number if + you're <emphasis>not</emphasis> using port 22.</para> + </listitem> + <listitem><para id="x_48e">The remainder of the URL is the local path to + the repository on the server.</para> + </listitem></orderedlist> + + <para id="x_48f">There's plenty of scope for confusion with the path + component of ssh URLs, as there is no standard way for tools + to interpret it. Some programs behave differently than others + when dealing with these paths. This isn't an ideal situation, + but it's unlikely to change. Please read the following + paragraphs carefully.</para> + + <para id="x_490">Mercurial treats the path to a repository on the server as + relative to the remote user's home directory. For example, if + user <literal>foo</literal> on the server has a home directory + of <filename class="directory">/home/foo</filename>, then an + ssh URL that contains a path component of <filename + class="directory">bar</filename> <emphasis>really</emphasis> + refers to the directory <filename + class="directory">/home/foo/bar</filename>.</para> + + <para id="x_491">If you want to specify a path relative to another user's + home directory, you can use a path that starts with a tilde + character followed by the user's name (let's call them + <literal>otheruser</literal>), like this.</para> + <programlisting>ssh://server/~otheruser/hg/repo</programlisting> + + <para id="x_492">And if you really want to specify an + <emphasis>absolute</emphasis> path on the server, begin the + path component with two slashes, as in this example.</para> + <programlisting>ssh://server//absolute/path</programlisting> + </sect2> + + <sect2> + <title>Finding an ssh client for your system</title> + + <para id="x_493">Almost every Unix-like system comes with OpenSSH + preinstalled. If you're using such a system, run + <literal>which ssh</literal> to find out if the + <command>ssh</command> command is installed (it's usually in + <filename class="directory">/usr/bin</filename>). In the + unlikely event that it isn't present, take a look at your + system documentation to figure out how to install it.</para> + + <para id="x_494">On Windows, the TortoiseHg package is bundled + with a version of Simon Tatham's excellent + <command>plink</command> command, and you should not need to + do any further configuration.</para> + </sect2> + + <sect2> + <title>Generating a key pair</title> + + <para id="x_499">To avoid the need to repetitively type a + password every time you need to use your ssh client, I + recommend generating a key pair.</para> + + <tip> + <title>Key pairs are not mandatory</title> + + <para id="x_6a4">Mercurial knows nothing about ssh authentication or key + pairs. You can, if you like, safely ignore this section and + the one that follows until you grow tired of repeatedly + typing ssh passwords.</para> + </tip> + + <itemizedlist> + <listitem> + <para id="x_6a5">On a Unix-like system, the + <command>ssh-keygen</command> command will do the + trick.</para> + <para id="x_6a6">On Windows, if you're using TortoiseHg, you may need + to download a command named <command>puttygen</command> + from <ulink + url="http://www.chiark.greenend.org.uk/~sgtatham/putty">the + PuTTY web site</ulink> to generate a key pair. See + <ulink + url="http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter8.html#pubkey-puttygen">the + <command>puttygen</command> documentation</ulink> for + details of how use the command.</para> + </listitem> + </itemizedlist> + + <para id="x_49a">When you generate a key pair, it's usually + <emphasis>highly</emphasis> advisable to protect it with a + passphrase. (The only time that you might not want to do this + is when you're using the ssh protocol for automated tasks on a + secure network.)</para> + + <para id="x_49b">Simply generating a key pair isn't enough, however. + You'll need to add the public key to the set of authorised + keys for whatever user you're logging in remotely as. For + servers using OpenSSH (the vast majority), this will mean + adding the public key to a list in a file called <filename + role="special">authorized_keys</filename> in their <filename + role="special" class="directory">.ssh</filename> + directory.</para> + + <para id="x_49c">On a Unix-like system, your public key will have a + <filename>.pub</filename> extension. If you're using + <command>puttygen</command> on Windows, you can save the + public key to a file of your choosing, or paste it from the + window it's displayed in straight into the <filename + role="special">authorized_keys</filename> file.</para> + </sect2> + <sect2> + <title>Using an authentication agent</title> + + <para id="x_49d">An authentication agent is a daemon that stores + passphrases in memory (so it will forget passphrases if you + log out and log back in again). An ssh client will notice if + it's running, and query it for a passphrase. If there's no + authentication agent running, or the agent doesn't store the + necessary passphrase, you'll have to type your passphrase + every time Mercurial tries to communicate with a server on + your behalf (e.g. whenever you pull or push changes).</para> + + <para id="x_49e">The downside of storing passphrases in an agent is that + it's possible for a well-prepared attacker to recover the + plain text of your passphrases, in some cases even if your + system has been power-cycled. You should make your own + judgment as to whether this is an acceptable risk. It + certainly saves a lot of repeated typing.</para> + + <itemizedlist> + <listitem> + <para id="x_49f">On Unix-like systems, the agent is called + <command>ssh-agent</command>, and it's often run + automatically for you when you log in. You'll need to use + the <command>ssh-add</command> command to add passphrases + to the agent's store.</para> + </listitem> + <listitem> + <para id="x_6a7">On Windows, if you're using TortoiseHg, the + <command>pageant</command> command acts as the agent. As + with <command>puttygen</command>, you'll need to <ulink + url="http://www.chiark.greenend.org.uk/%7Esgtatham/putty/download.html">download + <command>pageant</command></ulink> from the PuTTY web + site and read <ulink + url="http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter9.html#pageant">its + documentation</ulink>. The <command>pageant</command> + command adds an icon to your system tray that will let you + manage stored passphrases.</para> + </listitem> + </itemizedlist> + </sect2> + + <sect2> + <title>Configuring the server side properly</title> + + <para id="x_4a0">Because ssh can be fiddly to set up if you're new to it, + a variety of things can go wrong. Add Mercurial + on top, and there's plenty more scope for head-scratching. + Most of these potential problems occur on the server side, not + the client side. The good news is that once you've gotten a + configuration working, it will usually continue to work + indefinitely.</para> + + <para id="x_4a1">Before you try using Mercurial to talk to an ssh server, + it's best to make sure that you can use the normal + <command>ssh</command> or <command>putty</command> command to + talk to the server first. If you run into problems with using + these commands directly, Mercurial surely won't work. Worse, + it will obscure the underlying problem. Any time you want to + debug ssh-related Mercurial problems, you should drop back to + making sure that plain ssh client commands work first, + <emphasis>before</emphasis> you worry about whether there's a + problem with Mercurial.</para> + + <para id="x_4a2">The first thing to be sure of on the server side is that + you can actually log in from another machine at all. If you + can't use <command>ssh</command> or <command>putty</command> + to log in, the error message you get may give you a few hints + as to what's wrong. The most common problems are as + follows.</para> + <itemizedlist> + <listitem><para id="x_4a3">If you get a <quote>connection refused</quote> + error, either there isn't an SSH daemon running on the + server at all, or it's inaccessible due to firewall + configuration.</para> + </listitem> + <listitem><para id="x_4a4">If you get a <quote>no route to host</quote> + error, you either have an incorrect address for the server + or a seriously locked down firewall that won't admit its + existence at all.</para> + </listitem> + <listitem><para id="x_4a5">If you get a <quote>permission denied</quote> + error, you may have mistyped the username on the server, + or you could have mistyped your key's passphrase or the + remote user's password.</para> + </listitem></itemizedlist> + <para id="x_4a6">In summary, if you're having trouble talking to the + server's ssh daemon, first make sure that one is running at + all. On many systems it will be installed, but disabled, by + default. Once you're done with this step, you should then + check that the server's firewall is configured to allow + incoming connections on the port the ssh daemon is listening + on (usually 22). Don't worry about more exotic possibilities + for misconfiguration until you've checked these two + first.</para> + + <para id="x_4a7">If you're using an authentication agent on the client side + to store passphrases for your keys, you ought to be able to + log into the server without being prompted for a passphrase or + a password. If you're prompted for a passphrase, there are a + few possible culprits.</para> + <itemizedlist> + <listitem><para id="x_4a8">You might have forgotten to use + <command>ssh-add</command> or <command>pageant</command> + to store the passphrase.</para> + </listitem> + <listitem><para id="x_4a9">You might have stored the passphrase for the + wrong key.</para> + </listitem></itemizedlist> + <para id="x_4aa">If you're being prompted for the remote user's password, + there are another few possible problems to check.</para> + <itemizedlist> + <listitem><para id="x_4ab">Either the user's home directory or their + <filename role="special" class="directory">.ssh</filename> + directory might have excessively liberal permissions. As + a result, the ssh daemon will not trust or read their + <filename role="special">authorized_keys</filename> file. + For example, a group-writable home or <filename + role="special" class="directory">.ssh</filename> + directory will often cause this symptom.</para> + </listitem> + <listitem><para id="x_4ac">The user's <filename + role="special">authorized_keys</filename> file may have + a problem. If anyone other than the user owns or can write + to that file, the ssh daemon will not trust or read + it.</para> + </listitem></itemizedlist> + + <para id="x_4ad">In the ideal world, you should be able to run the + following command successfully, and it should print exactly + one line of output, the current date and time.</para> + <programlisting>ssh myserver date</programlisting> + + <para id="x_4ae">If, on your server, you have login scripts that print + banners or other junk even when running non-interactive + commands like this, you should fix them before you continue, + so that they only print output if they're run interactively. + Otherwise these banners will at least clutter up Mercurial's + output. Worse, they could potentially cause problems with + running Mercurial commands remotely. Mercurial tries to + detect and ignore banners in non-interactive + <command>ssh</command> sessions, but it is not foolproof. (If + you're editing your login scripts on your server, the usual + way to see if a login script is running in an interactive + shell is to check the return code from the command + <literal>tty -s</literal>.)</para> + + <para id="x_4af">Once you've verified that plain old ssh is working with + your server, the next step is to ensure that Mercurial runs on + the server. The following command should run + successfully:</para> + + <programlisting>ssh myserver hg version</programlisting> + + <para id="x_4b0">If you see an error message instead of normal <command + role="hg-cmd">hg version</command> output, this is usually + because you haven't installed Mercurial to <filename + class="directory">/usr/bin</filename>. Don't worry if this + is the case; you don't need to do that. But you should check + for a few possible problems.</para> + <itemizedlist> + <listitem><para id="x_4b1">Is Mercurial really installed on the server at + all? I know this sounds trivial, but it's worth + checking!</para> + </listitem> + <listitem><para id="x_4b2">Maybe your shell's search path (usually set + via the <envar>PATH</envar> environment variable) is + simply misconfigured.</para> + </listitem> + <listitem><para id="x_4b3">Perhaps your <envar>PATH</envar> environment + variable is only being set to point to the location of the + <command>hg</command> executable if the login session is + interactive. This can happen if you're setting the path + in the wrong shell login script. See your shell's + documentation for details.</para> + </listitem> + <listitem><para id="x_4b4">The <envar>PYTHONPATH</envar> environment + variable may need to contain the path to the Mercurial + Python modules. It might not be set at all; it could be + incorrect; or it may be set only if the login is + interactive.</para> + </listitem></itemizedlist> + + <para id="x_4b5">If you can run <command role="hg-cmd">hg version</command> + over an ssh connection, well done! You've got the server and + client sorted out. You should now be able to use Mercurial to + access repositories hosted by that username on that server. + If you run into problems with Mercurial and ssh at this point, + try using the <option role="hg-opt-global">--debug</option> + option to get a clearer picture of what's going on.</para> + </sect2> + <sect2> + <title>Using compression with ssh</title> + + <para id="x_4b6">Mercurial does not compress data when it uses the ssh + protocol, because the ssh protocol can transparently compress + data. However, the default behavior of ssh clients is + <emphasis>not</emphasis> to request compression.</para> + + <para id="x_4b7">Over any network other than a fast LAN (even a wireless + network), using compression is likely to significantly speed + up Mercurial's network operations. For example, over a WAN, + someone measured compression as reducing the amount of time + required to clone a particularly large repository from 51 + minutes to 17 minutes.</para> + + <para id="x_4b8">Both <command>ssh</command> and <command>plink</command> + accept a <option role="cmd-opt-ssh">-C</option> option which + turns on compression. You can easily edit your <filename + role="special">~/.hgrc</filename> to enable compression for + all of Mercurial's uses of the ssh protocol. Here is how to + do so for regular <command>ssh</command> on Unix-like systems, + for example.</para> + <programlisting>[ui] +ssh = ssh -C</programlisting> + + <para id="x_4b9">If you use <command>ssh</command> on a + Unix-like system, you can configure it to always use + compression when talking to your server. To do this, edit + your <filename role="special">.ssh/config</filename> file + (which may not yet exist), as follows.</para> + + <programlisting>Host hg + Compression yes + HostName hg.example.com</programlisting> + + <para id="x_4ba">This defines a hostname alias, + <literal>hg</literal>. When you use that hostname on the + <command>ssh</command> command line or in a Mercurial + <literal>ssh</literal>-protocol URL, it will cause + <command>ssh</command> to connect to + <literal>hg.example.com</literal> and use compression. This + gives you both a shorter name to type and compression, each of + which is a good thing in its own right.</para> + </sect2> + </sect1> + + <sect1 id="sec:collab:cgi"> + <title>Serving over HTTP using CGI</title> + + <para id="x_6a8">The simplest way to host one or more repositories in a + permanent way is to use a web server and Mercurial's CGI + support.</para> + + <para id="x_4bb">Depending on how ambitious you are, configuring Mercurial's + CGI interface can take anything from a few moments to several + hours.</para> + + <para id="x_4bc">We'll begin with the simplest of examples, and work our way + towards a more complex configuration. Even for the most basic + case, you're almost certainly going to need to read and modify + your web server's configuration.</para> + + <note> + <title>High pain tolerance required</title> + + <para id="x_4bd">Configuring a web server is a complex, fiddly, + and highly system-dependent activity. I can't possibly give + you instructions that will cover anything like all of the + cases you will encounter. Please use your discretion and + judgment in following the sections below. Be prepared to make + plenty of mistakes, and to spend a lot of time reading your + server's error logs.</para> + + <para id="x_6a9">If you don't have a strong stomach for tweaking + configurations over and over, or a compelling need to host + your own services, you might want to try one of the public + hosting services that I mentioned earlier.</para> + </note> + + <sect2> + <title>Web server configuration checklist</title> + + <para id="x_4be">Before you continue, do take a few moments to check a few + aspects of your system's setup.</para> + + <orderedlist> + <listitem><para id="x_4bf">Do you have a web server installed + at all? Mac OS X and some Linux distributions ship with + Apache, but many other systems may not have a web server + installed.</para> + </listitem> + <listitem><para id="x_4c0">If you have a web server installed, is it + actually running? On most systems, even if one is + present, it will be disabled by default.</para> + </listitem> + <listitem><para id="x_4c1">Is your server configured to allow you to run + CGI programs in the directory where you plan to do so? + Most servers default to explicitly disabling the ability + to run CGI programs.</para> + </listitem></orderedlist> + + <para id="x_4c2">If you don't have a web server installed, and don't have + substantial experience configuring Apache, you should consider + using the <literal>lighttpd</literal> web server instead of + Apache. Apache has a well-deserved reputation for baroque and + confusing configuration. While <literal>lighttpd</literal> is + less capable in some ways than Apache, most of these + capabilities are not relevant to serving Mercurial + repositories. And <literal>lighttpd</literal> is undeniably + <emphasis>much</emphasis> easier to get started with than + Apache.</para> + </sect2> + + <sect2> + <title>Basic CGI configuration</title> + + <para id="x_4c3">On Unix-like systems, it's common for users to have a + subdirectory named something like <filename + class="directory">public_html</filename> in their home + directory, from which they can serve up web pages. A file + named <filename>foo</filename> in this directory will be + accessible at a URL of the form + <literal>http://www.example.com/username/foo</literal>.</para> + + <para id="x_4c4">To get started, find the <filename + role="special">hgweb.cgi</filename> script that should be + present in your Mercurial installation. If you can't quickly + find a local copy on your system, simply download one from the + master Mercurial repository at <ulink + url="http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi">http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi</ulink>.</para> + + <para id="x_4c5">You'll need to copy this script into your <filename + class="directory">public_html</filename> directory, and + ensure that it's executable.</para> + <programlisting>cp .../hgweb.cgi ~/public_html +chmod 755 ~/public_html/hgweb.cgi</programlisting> + <para id="x_4c6">The <literal>755</literal> argument to + <command>chmod</command> is a little more general than just + making the script executable: it ensures that the script is + executable by anyone, and that <quote>group</quote> and + <quote>other</quote> write permissions are + <emphasis>not</emphasis> set. If you were to leave those + write permissions enabled, Apache's <literal>suexec</literal> + subsystem would likely refuse to execute the script. In fact, + <literal>suexec</literal> also insists that the + <emphasis>directory</emphasis> in which the script resides + must not be writable by others.</para> + <programlisting>chmod 755 ~/public_html</programlisting> + + <sect3 id="sec:collab:wtf"> + <title>What could <emphasis>possibly</emphasis> go + wrong?</title> + + <para id="x_4c7">Once you've copied the CGI script into place, + go into a web browser, and try to open the URL + <literal>http://myhostname/~myuser/hgweb.cgi</literal>, + <emphasis>but</emphasis> brace yourself for instant failure. + There's a high probability that trying to visit this URL + will fail, and there are many possible reasons for this. In + fact, you're likely to stumble over almost every one of the + possible errors below, so please read carefully. The + following are all of the problems I ran into on a system + running Fedora 7, with a fresh installation of Apache, and a + user account that I created specially to perform this + exercise.</para> + + <para id="x_4c8">Your web server may have per-user directories disabled. + If you're using Apache, search your config file for a + <literal>UserDir</literal> directive. If there's none + present, per-user directories will be disabled. If one + exists, but its value is <literal>disabled</literal>, then + per-user directories will be disabled. Otherwise, the + string after <literal>UserDir</literal> gives the name of + the subdirectory that Apache will look in under your home + directory, for example <filename + class="directory">public_html</filename>.</para> + + <para id="x_4c9">Your file access permissions may be too restrictive. + The web server must be able to traverse your home directory + and directories under your <filename + class="directory">public_html</filename> directory, and + read files under the latter too. Here's a quick recipe to + help you to make your permissions more appropriate.</para> + <programlisting>chmod 755 ~ +find ~/public_html -type d -print0 | xargs -0r chmod 755 +find ~/public_html -type f -print0 | xargs -0r chmod 644</programlisting> + + <para id="x_4ca">The other possibility with permissions is that you might + get a completely empty window when you try to load the + script. In this case, it's likely that your access + permissions are <emphasis>too permissive</emphasis>. Apache's + <literal>suexec</literal> subsystem won't execute a script + that's group- or world-writable, for example.</para> + + <para id="x_4cb">Your web server may be configured to disallow execution + of CGI programs in your per-user web directory. Here's + Apache's default per-user configuration from my Fedora + system.</para> + + &ch06-apache-config.lst; + + <para id="x_4cc">If you find a similar-looking + <literal>Directory</literal> group in your Apache + configuration, the directive to look at inside it is + <literal>Options</literal>. Add <literal>ExecCGI</literal> + to the end of this list if it's missing, and restart the web + server.</para> + + <para id="x_4cd">If you find that Apache serves you the text of the CGI + script instead of executing it, you may need to either + uncomment (if already present) or add a directive like + this.</para> + <programlisting>AddHandler cgi-script .cgi</programlisting> + + <para id="x_4ce">The next possibility is that you might be served with a + colourful Python backtrace claiming that it can't import a + <literal>mercurial</literal>-related module. This is + actually progress! The server is now capable of executing + your CGI script. This error is only likely to occur if + you're running a private installation of Mercurial, instead + of a system-wide version. Remember that the web server runs + the CGI program without any of the environment variables + that you take for granted in an interactive session. If + this error happens to you, edit your copy of <filename + role="special">hgweb.cgi</filename> and follow the + directions inside it to correctly set your + <envar>PYTHONPATH</envar> environment variable.</para> + + <para id="x_4cf">Finally, you are <emphasis>certain</emphasis> to be + served with another colourful Python backtrace: this one + will complain that it can't find <filename + class="directory">/path/to/repository</filename>. Edit + your <filename role="special">hgweb.cgi</filename> script + and replace the <filename + class="directory">/path/to/repository</filename> string + with the complete path to the repository you want to serve + up.</para> + + <para id="x_4d0">At this point, when you try to reload the page, you + should be presented with a nice HTML view of your + repository's history. Whew!</para> + </sect3> + + <sect3> + <title>Configuring lighttpd</title> + + <para id="x_4d1">To be exhaustive in my experiments, I tried configuring + the increasingly popular <literal>lighttpd</literal> web + server to serve the same repository as I described with + Apache above. I had already overcome all of the problems I + outlined with Apache, many of which are not server-specific. + As a result, I was fairly sure that my file and directory + permissions were good, and that my <filename + role="special">hgweb.cgi</filename> script was properly + edited.</para> + + <para id="x_4d2">Once I had Apache running, getting + <literal>lighttpd</literal> to serve the repository was a + snap (in other words, even if you're trying to use + <literal>lighttpd</literal>, you should read the Apache + section). I first had to edit the + <literal>mod_access</literal> section of its config file to + enable <literal>mod_cgi</literal> and + <literal>mod_userdir</literal>, both of which were disabled + by default on my system. I then added a few lines to the + end of the config file, to configure these modules.</para> + <programlisting>userdir.path = "public_html" +cgi.assign = (".cgi" => "" )</programlisting> + <para id="x_4d3">With this done, <literal>lighttpd</literal> ran + immediately for me. If I had configured + <literal>lighttpd</literal> before Apache, I'd almost + certainly have run into many of the same system-level + configuration problems as I did with Apache. However, I + found <literal>lighttpd</literal> to be noticeably easier to + configure than Apache, even though I've used Apache for over + a decade, and this was my first exposure to + <literal>lighttpd</literal>.</para> + </sect3> + </sect2> + + <sect2> + <title>Sharing multiple repositories with one CGI script</title> + + <para id="x_4d4">The <filename role="special">hgweb.cgi</filename> script + only lets you publish a single repository, which is an + annoying restriction. If you want to publish more than one + without wracking yourself with multiple copies of the same + script, each with different names, a better choice is to use + the <filename role="special">hgwebdir.cgi</filename> + script.</para> + + <para id="x_4d5">The procedure to configure <filename + role="special">hgwebdir.cgi</filename> is only a little more + involved than for <filename + role="special">hgweb.cgi</filename>. First, you must obtain + a copy of the script. If you don't have one handy, you can + download a copy from the master Mercurial repository at <ulink + url="http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi">http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi</ulink>.</para> + + <para id="x_4d6">You'll need to copy this script into your <filename + class="directory">public_html</filename> directory, and + ensure that it's executable.</para> + + <programlisting>cp .../hgwebdir.cgi ~/public_html +chmod 755 ~/public_html ~/public_html/hgwebdir.cgi</programlisting> + + <para id="x_4d7">With basic configuration out of the way, try to + visit <literal>http://myhostname/~myuser/hgwebdir.cgi</literal> + in your browser. It should + display an empty list of repositories. If you get a blank + window or error message, try walking through the list of + potential problems in <xref + linkend="sec:collab:wtf"/>.</para> + + <para id="x_4d8">The <filename role="special">hgwebdir.cgi</filename> + script relies on an external configuration file. By default, + it searches for a file named <filename + role="special">hgweb.config</filename> in the same directory + as itself. You'll need to create this file, and make it + world-readable. The format of the file is similar to a + Windows <quote>ini</quote> file, as understood by Python's + <literal>ConfigParser</literal> + <citation>web:configparser</citation> module.</para> + + <para id="x_4d9">The easiest way to configure <filename + role="special">hgwebdir.cgi</filename> is with a section + named <literal>collections</literal>. This will automatically + publish <emphasis>every</emphasis> repository under the + directories you name. The section should look like + this:</para> + <programlisting>[collections] +/my/root = /my/root</programlisting> + <para id="x_4da">Mercurial interprets this by looking at the directory name + on the <emphasis>right</emphasis> hand side of the + <quote><literal>=</literal></quote> sign; finding repositories + in that directory hierarchy; and using the text on the + <emphasis>left</emphasis> to strip off matching text from the + names it will actually list in the web interface. The + remaining component of a path after this stripping has + occurred is called a <quote>virtual path</quote>.</para> + + <para id="x_4db">Given the example above, if we have a + repository whose local path is <filename + class="directory">/my/root/this/repo</filename>, the CGI + script will strip the leading <filename + class="directory">/my/root</filename> from the name, and + publish the repository with a virtual path of <filename + class="directory">this/repo</filename>. If the base URL for + our CGI script is + <literal>http://myhostname/~myuser/hgwebdir.cgi</literal>, the + complete URL for that repository will be + <literal>http://myhostname/~myuser/hgwebdir.cgi/this/repo</literal>.</para> + + <para id="x_4dc">If we replace <filename + class="directory">/my/root</filename> on the left hand side + of this example with <filename + class="directory">/my</filename>, then <filename + role="special">hgwebdir.cgi</filename> will only strip off + <filename class="directory">/my</filename> from the repository + name, and will give us a virtual path of <filename + class="directory">root/this/repo</filename> instead of + <filename class="directory">this/repo</filename>.</para> + + <para id="x_4dd">The <filename role="special">hgwebdir.cgi</filename> + script will recursively search each directory listed in the + <literal>collections</literal> section of its configuration + file, but it will <literal>not</literal> recurse into the + repositories it finds.</para> + + <para id="x_4de">The <literal>collections</literal> mechanism makes it easy + to publish many repositories in a <quote>fire and + forget</quote> manner. You only need to set up the CGI + script and configuration file one time. Afterwards, you can + publish or unpublish a repository at any time by simply moving + it into, or out of, the directory hierarchy in which you've + configured <filename role="special">hgwebdir.cgi</filename> to + look.</para> + + <sect3> + <title>Explicitly specifying which repositories to + publish</title> + + <para id="x_4df">In addition to the <literal>collections</literal> + mechanism, the <filename + role="special">hgwebdir.cgi</filename> script allows you + to publish a specific list of repositories. To do so, + create a <literal>paths</literal> section, with contents of + the following form.</para> + <programlisting>[paths] +repo1 = /my/path/to/some/repo +repo2 = /some/path/to/another</programlisting> + <para id="x_4e0">In this case, the virtual path (the component that will + appear in a URL) is on the left hand side of each + definition, while the path to the repository is on the + right. Notice that there does not need to be any + relationship between the virtual path you choose and the + location of a repository in your filesystem.</para> + + <para id="x_4e1">If you wish, you can use both the + <literal>collections</literal> and <literal>paths</literal> + mechanisms simultaneously in a single configuration + file.</para> + + <note> + <title>Beware duplicate virtual paths</title> + + <para id="x_4e2"> If several repositories have the same + virtual path, <filename + role="special">hgwebdir.cgi</filename> will not report + an error. Instead, it will behave unpredictably.</para> + </note> + </sect3> + </sect2> + + <sect2> + <title>Downloading source archives</title> + + <para id="x_4e3">Mercurial's web interface lets users download an archive + of any revision. This archive will contain a snapshot of the + working directory as of that revision, but it will not contain + a copy of the repository data.</para> + + <para id="x_4e4">By default, this feature is not enabled. To enable it, + you'll need to add an <envar + role="rc-item-web">allow_archive</envar> item to the + <literal role="rc-web">web</literal> section of your <filename + role="special">~/.hgrc</filename>; see below for details.</para> + </sect2> + <sect2> + <title>Web configuration options</title> + + <para id="x_4e5">Mercurial's web interfaces (the <command role="hg-cmd">hg + serve</command> command, and the <filename + role="special">hgweb.cgi</filename> and <filename + role="special">hgwebdir.cgi</filename> scripts) have a + number of configuration options that you can set. These + belong in a section named <literal + role="rc-web">web</literal>.</para> + <itemizedlist> + <listitem><para id="x_4e6"><envar + role="rc-item-web">allow_archive</envar>: Determines + which (if any) archive download mechanisms Mercurial + supports. If you enable this feature, users of the web + interface will be able to download an archive of whatever + revision of a repository they are viewing. To enable the + archive feature, this item must take the form of a + sequence of words drawn from the list below.</para> + <itemizedlist> + <listitem><para id="x_4e7"><literal>bz2</literal>: A + <command>tar</command> archive, compressed using + <literal>bzip2</literal> compression. This has the + best compression ratio, but uses the most CPU time on + the server.</para> + </listitem> + <listitem><para id="x_4e8"><literal>gz</literal>: A + <command>tar</command> archive, compressed using + <literal>gzip</literal> compression.</para> + </listitem> + <listitem><para id="x_4e9"><literal>zip</literal>: A + <command>zip</command> archive, compressed using LZW + compression. This format has the worst compression + ratio, but is widely used in the Windows world.</para> + </listitem> + </itemizedlist> + <para id="x_4ea"> If you provide an empty list, or don't have an + <envar role="rc-item-web">allow_archive</envar> entry at + all, this feature will be disabled. Here is an example of + how to enable all three supported formats.</para> + <programlisting>[web] +allow_archive = bz2 gz zip</programlisting> + </listitem> + <listitem><para id="x_4eb"><envar role="rc-item-web">allowpull</envar>: + Boolean. Determines whether the web interface allows + remote users to <command role="hg-cmd">hg pull</command> + and <command role="hg-cmd">hg clone</command> this + repository over HTTP. If set to <literal>no</literal> or + <literal>false</literal>, only the + <quote>human-oriented</quote> portion of the web interface + is available.</para> + </listitem> + <listitem><para id="x_4ec"><envar role="rc-item-web">contact</envar>: + String. A free-form (but preferably brief) string + identifying the person or group in charge of the + repository. This often contains the name and email + address of a person or mailing list. It often makes sense + to place this entry in a repository's own <filename + role="special">.hg/hgrc</filename> file, but it can make + sense to use in a global <filename + role="special">~/.hgrc</filename> if every repository + has a single maintainer.</para> + </listitem> + <listitem><para id="x_4ed"><envar role="rc-item-web">maxchanges</envar>: + Integer. The default maximum number of changesets to + display in a single page of output.</para> + </listitem> + <listitem><para id="x_4ee"><envar role="rc-item-web">maxfiles</envar>: + Integer. The default maximum number of modified files to + display in a single page of output.</para> + </listitem> + <listitem><para id="x_4ef"><envar role="rc-item-web">stripes</envar>: + Integer. If the web interface displays alternating + <quote>stripes</quote> to make it easier to visually align + rows when you are looking at a table, this number controls + the number of rows in each stripe.</para> + </listitem> + <listitem><para id="x_4f0"><envar + role="rc-item-web">style</envar>: Controls the template + Mercurial uses to display the web interface. Mercurial + ships with several web templates.</para> + <itemizedlist> + <listitem> + <para id="x_6aa"><literal>coal</literal> is monochromatic.</para> + </listitem> + <listitem> + <para id="x_6ab"><literal>gitweb</literal> emulates the visual + style of git's web interface.</para> + </listitem> + <listitem> + <para id="x_6ac"><literal>monoblue</literal> uses solid blues and + greys.</para> + </listitem> + <listitem> + <para id="x_6ad"><literal>paper</literal> is the default.</para> + </listitem> + <listitem> + <para id="x_6ae"><literal>spartan</literal> was the default for a + long time.</para> + </listitem> + </itemizedlist> + <para id="x_6af">You can + also specify a custom template of your own; see + <xref linkend="chap:template"/> for details. Here, you can + see how to enable the <literal>gitweb</literal> + style.</para> + <programlisting>[web] +style = gitweb</programlisting> + </listitem> + <listitem><para id="x_4f1"><envar role="rc-item-web">templates</envar>: + Path. The directory in which to search for template + files. By default, Mercurial searches in the directory in + which it was installed.</para> + </listitem></itemizedlist> + <para id="x_4f2">If you are using <filename + role="special">hgwebdir.cgi</filename>, you can place a few + configuration items in a <literal role="rc-web">web</literal> + section of the <filename + role="special">hgweb.config</filename> file instead of a + <filename role="special">~/.hgrc</filename> file, for + convenience. These items are <envar + role="rc-item-web">motd</envar> and <envar + role="rc-item-web">style</envar>.</para> + + <sect3> + <title>Options specific to an individual repository</title> + + <para id="x_4f3">A few <literal role="rc-web">web</literal> configuration + items ought to be placed in a repository's local <filename + role="special">.hg/hgrc</filename>, rather than a user's + or global <filename role="special">~/.hgrc</filename>.</para> + <itemizedlist> + <listitem><para id="x_4f4"><envar + role="rc-item-web">description</envar>: String. A + free-form (but preferably brief) string that describes + the contents or purpose of the repository.</para> + </listitem> + <listitem><para id="x_4f5"><envar role="rc-item-web">name</envar>: + String. The name to use for the repository in the web + interface. This overrides the default name, which is + the last component of the repository's path.</para> + </listitem></itemizedlist> + </sect3> + + <sect3> + <title>Options specific to the <command role="hg-cmd">hg + serve</command> command</title> + + <para id="x_4f6">Some of the items in the <literal + role="rc-web">web</literal> section of a <filename + role="special">~/.hgrc</filename> file are only for use + with the <command role="hg-cmd">hg serve</command> + command.</para> + <itemizedlist> + <listitem><para id="x_4f7"><envar role="rc-item-web">accesslog</envar>: + Path. The name of a file into which to write an access + log. By default, the <command role="hg-cmd">hg + serve</command> command writes this information to + standard output, not to a file. Log entries are written + in the standard <quote>combined</quote> file format used + by almost all web servers.</para> + </listitem> + <listitem><para id="x_4f8"><envar role="rc-item-web">address</envar>: + String. The local address on which the server should + listen for incoming connections. By default, the server + listens on all addresses.</para> + </listitem> + <listitem><para id="x_4f9"><envar role="rc-item-web">errorlog</envar>: + Path. The name of a file into which to write an error + log. By default, the <command role="hg-cmd">hg + serve</command> command writes this information to + standard error, not to a file.</para> + </listitem> + <listitem><para id="x_4fa"><envar role="rc-item-web">ipv6</envar>: + Boolean. Whether to use the IPv6 protocol. By default, + IPv6 is not used.</para> + </listitem> + <listitem><para id="x_4fb"><envar role="rc-item-web">port</envar>: + Integer. The TCP port number on which the server should + listen. The default port number used is 8000.</para> + </listitem></itemizedlist> + </sect3> + + <sect3> + <title>Choosing the right <filename + role="special">~/.hgrc</filename> file to add <literal + role="rc-web">web</literal> items to</title> + + <para id="x_4fc">It is important to remember that a web server like + Apache or <literal>lighttpd</literal> will run under a user + ID that is different to yours. CGI scripts run by your + server, such as <filename + role="special">hgweb.cgi</filename>, will usually also run + under that user ID.</para> + + <para id="x_4fd">If you add <literal role="rc-web">web</literal> items to + your own personal <filename role="special">~/.hgrc</filename> file, CGI scripts won't read that + <filename role="special">~/.hgrc</filename> file. Those + settings will thus only affect the behavior of the <command + role="hg-cmd">hg serve</command> command when you run it. + To cause CGI scripts to see your settings, either create a + <filename role="special">~/.hgrc</filename> file in the + home directory of the user ID that runs your web server, or + add those settings to a system-wide <filename + role="special">hgrc</filename> file.</para> + </sect3> + </sect2> + </sect1> + + <sect1> + <title>System-wide configuration</title> + + <para id="x_6b0">On Unix-like systems shared by multiple users (such as a + server to which people publish changes), it often makes sense to + set up some global default behaviors, such as what theme to use + in web interfaces.</para> + + <para id="x_6b1">If a file named <filename>/etc/mercurial/hgrc</filename> + exists, Mercurial will read it at startup time and apply any + configuration settings it finds in that file. It will also look + for files ending in a <literal>.rc</literal> extension in a + directory named <filename>/etc/mercurial/hgrc.d</filename>, and + apply any configuration settings it finds in each of those + files.</para> + + <sect2> + <title>Making Mercurial more trusting</title> + + <para id="x_6b2">One situation in which a global <filename>hgrc</filename> + can be useful is if users are pulling changes owned by other + users. By default, Mercurial will not trust most of the + configuration items in a <filename>.hg/hgrc</filename> file + inside a repository that is owned by a different user. If we + clone or pull changes from such a repository, Mercurial will + print a warning stating that it does not trust their + <filename>.hg/hgrc</filename>.</para> + + <para id="x_6b3">If everyone in a particular Unix group is on the same team + and <emphasis>should</emphasis> trust each other's + configuration settings, or we want to trust particular users, + we can override Mercurial's skeptical defaults by creating a + system-wide <filename>hgrc</filename> file such as the + following:</para> + + <programlisting># Save this as e.g. /etc/mercurial/hgrc.d/trust.rc +[trusted] +# Trust all entries in any hgrc file owned by the "editors" or +# "www-data" groups. +groups = editors, www-data + +# Trust entries in hgrc files owned by the following users. +users = apache, bobo </programlisting> -<orderedlist> -<listitem><para>The <quote><literal>ssh://</literal></quote> part tells Mercurial to use the ssh - protocol. -</para> -</listitem> -<listitem><para>The <quote><literal>bos@</literal></quote> component indicates what username to log - into the server as. You can leave this out if the remote username - is the same as your local username. -</para> -</listitem> -<listitem><para>The <quote><literal>hg.serpentine.com</literal></quote> gives the hostname of the - server to log into. -</para> -</listitem> -<listitem><para>The <quote>:22</quote> identifies the port number to connect to the server - on. The default port is 22, so you only need to specify this part - if you're <emphasis>not</emphasis> using port 22. -</para> -</listitem> -<listitem><para>The remainder of the URL is the local path to the repository on - the server. -</para> -</listitem></orderedlist> - -<para>There's plenty of scope for confusion with the path component of ssh -URLs, as there is no standard way for tools to interpret it. Some -programs behave differently than others when dealing with these paths. -This isn't an ideal situation, but it's unlikely to change. Please -read the following paragraphs carefully. -</para> - -<para>Mercurial treats the path to a repository on the server as relative to -the remote user's home directory. For example, if user <literal>foo</literal> -on the server has a home directory of <filename class="directory">/home/foo</filename>, then an ssh -URL that contains a path component of <filename class="directory">bar</filename> -<emphasis>really</emphasis> refers to the directory <filename class="directory">/home/foo/bar</filename>. -</para> - -<para>If you want to specify a path relative to another user's home -directory, you can use a path that starts with a tilde character -followed by the user's name (let's call them <literal>otheruser</literal>), like -this. -</para> -<programlisting> -<para> ssh://server/ otheruser/hg/repo -</para> -</programlisting> - -<para>And if you really want to specify an <emphasis>absolute</emphasis> path on the -server, begin the path component with two slashes, as in this example. -</para> -<programlisting> -<para> ssh://server//absolute/path -</para> -</programlisting> - -</sect2> -<sect2> -<title>Finding an ssh client for your system</title> - -<para>Almost every Unix-like system comes with OpenSSH preinstalled. If -you're using such a system, run <literal>which ssh</literal> to find out if -the <command>ssh</command> command is installed (it's usually in -<filename class="directory">/usr/bin</filename>). In the unlikely event that it isn't present, -take a look at your system documentation to figure out how to install -it. -</para> - -<para>On Windows, you'll first need to download a suitable ssh -client. There are two alternatives. -</para> -<itemizedlist> -<listitem><para>Simon Tatham's excellent PuTTY package <citation>web:putty</citation> provides - a complete suite of ssh client commands. -</para> -</listitem> -<listitem><para>If you have a high tolerance for pain, you can use the Cygwin - port of OpenSSH. -</para> -</listitem></itemizedlist> -<para>In either case, you'll need to edit your \hgini\ file to tell -Mercurial where to find the actual client command. For example, if -you're using PuTTY, you'll need to use the <command>plink</command> command as -a command-line ssh client. -</para> -<programlisting> -<para> [ui] - ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key" -</para> -</programlisting> - -<note> -<para> The path to <command>plink</command> shouldn't contain any whitespace - characters, or Mercurial may not be able to run it correctly (so - putting it in <filename class="directory">C:\\Program Files</filename> is probably not a good - idea). -</para> -</note> - -</sect2> -<sect2> -<title>Generating a key pair</title> - -<para>To avoid the need to repetitively type a password every time you need -to use your ssh client, I recommend generating a key pair. On a -Unix-like system, the <command>ssh-keygen</command> command will do the trick. -On Windows, if you're using PuTTY, the <command>puttygen</command> command is -what you'll need. -</para> - -<para>When you generate a key pair, it's usually <emphasis>highly</emphasis> advisable to -protect it with a passphrase. (The only time that you might not want -to do this is when you're using the ssh protocol for automated tasks -on a secure network.) -</para> - -<para>Simply generating a key pair isn't enough, however. You'll need to -add the public key to the set of authorised keys for whatever user -you're logging in remotely as. For servers using OpenSSH (the vast -majority), this will mean adding the public key to a list in a file -called <filename role="special">authorized_keys</filename> in their <filename role="special" class="directory">.ssh</filename> -directory. -</para> - -<para>On a Unix-like system, your public key will have a <filename>.pub</filename> -extension. If you're using <command>puttygen</command> on Windows, you can -save the public key to a file of your choosing, or paste it from the -window it's displayed in straight into the -<filename role="special">authorized_keys</filename> file. -</para> - -</sect2> -<sect2> -<title>Using an authentication agent</title> - -<para>An authentication agent is a daemon that stores passphrases in memory -(so it will forget passphrases if you log out and log back in again). -An ssh client will notice if it's running, and query it for a -passphrase. If there's no authentication agent running, or the agent -doesn't store the necessary passphrase, you'll have to type your -passphrase every time Mercurial tries to communicate with a server on -your behalf (e.g. whenever you pull or push changes). -</para> - -<para>The downside of storing passphrases in an agent is that it's possible -for a well-prepared attacker to recover the plain text of your -passphrases, in some cases even if your system has been power-cycled. -You should make your own judgment as to whether this is an acceptable -risk. It certainly saves a lot of repeated typing. -</para> - -<para>On Unix-like systems, the agent is called <command>ssh-agent</command>, and -it's often run automatically for you when you log in. You'll need to -use the <command>ssh-add</command> command to add passphrases to the agent's -store. On Windows, if you're using PuTTY, the <command>pageant</command> -command acts as the agent. It adds an icon to your system tray that -will let you manage stored passphrases. -</para> - -</sect2> -<sect2> -<title>Configuring the server side properly</title> - -<para>Because ssh can be fiddly to set up if you're new to it, there's a -variety of things that can go wrong. Add Mercurial on top, and -there's plenty more scope for head-scratching. Most of these -potential problems occur on the server side, not the client side. The -good news is that once you've gotten a configuration working, it will -usually continue to work indefinitely. -</para> - -<para>Before you try using Mercurial to talk to an ssh server, it's best to -make sure that you can use the normal <command>ssh</command> or <command>putty</command> -command to talk to the server first. If you run into problems with -using these commands directly, Mercurial surely won't work. Worse, it -will obscure the underlying problem. Any time you want to debug -ssh-related Mercurial problems, you should drop back to making sure -that plain ssh client commands work first, <emphasis>before</emphasis> you worry -about whether there's a problem with Mercurial. -</para> - -<para>The first thing to be sure of on the server side is that you can -actually log in from another machine at all. If you can't use -<command>ssh</command> or <command>putty</command> to log in, the error message you get -may give you a few hints as to what's wrong. The most common problems -are as follows. -</para> -<itemizedlist> -<listitem><para>If you get a <quote>connection refused</quote> error, either there isn't an - SSH daemon running on the server at all, or it's inaccessible due to - firewall configuration. -</para> -</listitem> -<listitem><para>If you get a <quote>no route to host</quote> error, you either have an - incorrect address for the server or a seriously locked down firewall - that won't admit its existence at all. -</para> -</listitem> -<listitem><para>If you get a <quote>permission denied</quote> error, you may have mistyped - the username on the server, or you could have mistyped your key's - passphrase or the remote user's password. -</para> -</listitem></itemizedlist> -<para>In summary, if you're having trouble talking to the server's ssh -daemon, first make sure that one is running at all. On many systems -it will be installed, but disabled, by default. Once you're done with -this step, you should then check that the server's firewall is -configured to allow incoming connections on the port the ssh daemon is -listening on (usually 22). Don't worry about more exotic -possibilities for misconfiguration until you've checked these two -first. -</para> - -<para>If you're using an authentication agent on the client side to store -passphrases for your keys, you ought to be able to log into the server -without being prompted for a passphrase or a password. If you're -prompted for a passphrase, there are a few possible culprits. -</para> -<itemizedlist> -<listitem><para>You might have forgotten to use <command>ssh-add</command> or - <command>pageant</command> to store the passphrase. -</para> -</listitem> -<listitem><para>You might have stored the passphrase for the wrong key. -</para> -</listitem></itemizedlist> -<para>If you're being prompted for the remote user's password, there are -another few possible problems to check. -</para> -<itemizedlist> -<listitem><para>Either the user's home directory or their <filename role="special" class="directory">.ssh</filename> - directory might have excessively liberal permissions. As a result, - the ssh daemon will not trust or read their - <filename role="special">authorized_keys</filename> file. For example, a group-writable - home or <filename role="special" class="directory">.ssh</filename> directory will often cause this symptom. -</para> -</listitem> -<listitem><para>The user's <filename role="special">authorized_keys</filename> file may have a problem. - If anyone other than the user owns or can write to that file, the - ssh daemon will not trust or read it. -</para> -</listitem></itemizedlist> - -<para>In the ideal world, you should be able to run the following command -successfully, and it should print exactly one line of output, the -current date and time. -</para> -<programlisting> -<para> ssh myserver date -</para> -</programlisting> - -<para>If, on your server, you have login scripts that print banners or other -junk even when running non-interactive commands like this, you should -fix them before you continue, so that they only print output if -they're run interactively. Otherwise these banners will at least -clutter up Mercurial's output. Worse, they could potentially cause -problems with running Mercurial commands remotely. Mercurial makes -tries to detect and ignore banners in non-interactive <command>ssh</command> -sessions, but it is not foolproof. (If you're editing your login -scripts on your server, the usual way to see if a login script is -running in an interactive shell is to check the return code from the -command <literal>tty -s</literal>.) -</para> - -<para>Once you've verified that plain old ssh is working with your server, -the next step is to ensure that Mercurial runs on the server. The -following command should run successfully: -</para> -<programlisting> -<para> ssh myserver hg version -</para> -</programlisting> -<para>If you see an error message instead of normal <command role="hg-cmd">hg version</command> output, -this is usually because you haven't installed Mercurial to -<filename class="directory">/usr/bin</filename>. Don't worry if this is the case; you don't need -to do that. But you should check for a few possible problems. -</para> -<itemizedlist> -<listitem><para>Is Mercurial really installed on the server at all? I know this - sounds trivial, but it's worth checking! -</para> -</listitem> -<listitem><para>Maybe your shell's search path (usually set via the <envar>PATH</envar> - environment variable) is simply misconfigured. -</para> -</listitem> -<listitem><para>Perhaps your <envar>PATH</envar> environment variable is only being set - to point to the location of the <command>hg</command> executable if the login - session is interactive. This can happen if you're setting the path - in the wrong shell login script. See your shell's documentation for - details. -</para> -</listitem> -<listitem><para>The <envar>PYTHONPATH</envar> environment variable may need to contain - the path to the Mercurial Python modules. It might not be set at - all; it could be incorrect; or it may be set only if the login is - interactive. -</para> -</listitem></itemizedlist> - -<para>If you can run <command role="hg-cmd">hg version</command> over an ssh connection, well done! -You've got the server and client sorted out. You should now be able -to use Mercurial to access repositories hosted by that username on -that server. If you run into problems with Mercurial and ssh at this -point, try using the <option role="hg-opt-global">--debug</option> option to get a clearer picture -of what's going on. -</para> - -</sect2> -<sect2> -<title>Using compression with ssh</title> - -<para>Mercurial does not compress data when it uses the ssh protocol, -because the ssh protocol can transparently compress data. However, -the default behaviour of ssh clients is <emphasis>not</emphasis> to request -compression. -</para> - -<para>Over any network other than a fast LAN (even a wireless network), -using compression is likely to significantly speed up Mercurial's -network operations. For example, over a WAN, someone measured -compression as reducing the amount of time required to clone a -particularly large repository from 51 minutes to 17 minutes. -</para> - -<para>Both <command>ssh</command> and <command>plink</command> accept a <option role="cmd-opt-ssh">-C</option> -option which turns on compression. You can easily edit your <filename role="special"> /.hgrc</filename>\ to -enable compression for all of Mercurial's uses of the ssh protocol. -</para> -<programlisting> -<para> [ui] - ssh = ssh -C -</para> -</programlisting> - -<para>If you use <command>ssh</command>, you can configure it to always use -compression when talking to your server. To do this, edit your -<filename role="special">.ssh/config</filename> file (which may not yet exist), as follows. -</para> -<programlisting> -<para> Host hg - Compression yes - HostName hg.example.com -</para> -</programlisting> -<para>This defines an alias, <literal>hg</literal>. When you use it on the -<command>ssh</command> command line or in a Mercurial <literal>ssh</literal>-protocol -URL, it will cause <command>ssh</command> to connect to <literal>hg.example.com</literal> -and use compression. This gives you both a shorter name to type and -compression, each of which is a good thing in its own right. -</para> - -</sect2> -</sect1> -<sect1> -<title>Serving over HTTP using CGI</title> -<para>\label{sec:collab:cgi} -</para> - -<para>Depending on how ambitious you are, configuring Mercurial's CGI -interface can take anything from a few moments to several hours. -</para> - -<para>We'll begin with the simplest of examples, and work our way towards a -more complex configuration. Even for the most basic case, you're -almost certainly going to need to read and modify your web server's -configuration. -</para> - -<note> -<para> Configuring a web server is a complex, fiddly, and highly - system-dependent activity. I can't possibly give you instructions - that will cover anything like all of the cases you will encounter. - Please use your discretion and judgment in following the sections - below. Be prepared to make plenty of mistakes, and to spend a lot - of time reading your server's error logs. -</para> -</note> - -<sect2> -<title>Web server configuration checklist</title> - -<para>Before you continue, do take a few moments to check a few aspects of -your system's setup. -</para> - -<orderedlist> -<listitem><para>Do you have a web server installed at all? Mac OS X ships with - Apache, but many other systems may not have a web server installed. -</para> -</listitem> -<listitem><para>If you have a web server installed, is it actually running? On - most systems, even if one is present, it will be disabled by - default. -</para> -</listitem> -<listitem><para>Is your server configured to allow you to run CGI programs in - the directory where you plan to do so? Most servers default to - explicitly disabling the ability to run CGI programs. -</para> -</listitem></orderedlist> - -<para>If you don't have a web server installed, and don't have substantial -experience configuring Apache, you should consider using the -<literal>lighttpd</literal> web server instead of Apache. Apache has a -well-deserved reputation for baroque and confusing configuration. -While <literal>lighttpd</literal> is less capable in some ways than Apache, most -of these capabilities are not relevant to serving Mercurial -repositories. And <literal>lighttpd</literal> is undeniably <emphasis>much</emphasis> easier -to get started with than Apache. -</para> - -</sect2> -<sect2> -<title>Basic CGI configuration</title> - -<para>On Unix-like systems, it's common for users to have a subdirectory -named something like <filename class="directory">public_html</filename> in their home directory, -from which they can serve up web pages. A file named <filename>foo</filename> -in this directory will be accessible at a URL of the form -<literal>http://www.example.com/\ {</literal>username/foo}. -</para> - -<para>To get started, find the <filename role="special">hgweb.cgi</filename> script that should be -present in your Mercurial installation. If you can't quickly find a -local copy on your system, simply download one from the master -Mercurial repository at -<ulink url="http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi">http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi</ulink>. -</para> - -<para>You'll need to copy this script into your <filename class="directory">public_html</filename> -directory, and ensure that it's executable. -</para> -<programlisting> -<para> cp .../hgweb.cgi /public_html - chmod 755 /public_html/hgweb.cgi -</para> -</programlisting> -<para>The <literal>755</literal> argument to <command>chmod</command> is a little more general -than just making the script executable: it ensures that the script is -executable by anyone, and that <quote>group</quote> and <quote>other</quote> write -permissions are <emphasis>not</emphasis> set. If you were to leave those write -permissions enabled, Apache's <literal>suexec</literal> subsystem would likely -refuse to execute the script. In fact, <literal>suexec</literal> also insists -that the <emphasis>directory</emphasis> in which the script resides must not be -writable by others. -</para> -<programlisting> -<para> chmod 755 /public_html -</para> -</programlisting> - -<sect3> -<title>What could <emphasis>possibly</emphasis> go wrong?</title> -<para>\label{sec:collab:wtf} -</para> - -<para>Once you've copied the CGI script into place, go into a web browser, -and try to open the URL <ulink url="http://myhostname/ myuser/hgweb.cgi">http://myhostname/ myuser/hgweb.cgi</ulink>, -<emphasis>but</emphasis> brace yourself for instant failure. There's a high -probability that trying to visit this URL will fail, and there are -many possible reasons for this. In fact, you're likely to stumble -over almost every one of the possible errors below, so please read -carefully. The following are all of the problems I ran into on a -system running Fedora 7, with a fresh installation of Apache, and a -user account that I created specially to perform this exercise. -</para> - -<para>Your web server may have per-user directories disabled. If you're -using Apache, search your config file for a <literal>UserDir</literal> -directive. If there's none present, per-user directories will be -disabled. If one exists, but its value is <literal>disabled</literal>, then -per-user directories will be disabled. Otherwise, the string after -<literal>UserDir</literal> gives the name of the subdirectory that Apache will -look in under your home directory, for example <filename class="directory">public_html</filename>. -</para> - -<para>Your file access permissions may be too restrictive. The web server -must be able to traverse your home directory and directories under -your <filename class="directory">public_html</filename> directory, and read files under the latter -too. Here's a quick recipe to help you to make your permissions more -appropriate. -</para> -<programlisting> -<para> chmod 755 - find /public_html -type d -print0 | xargs -0r chmod 755 - find /public_html -type f -print0 | xargs -0r chmod 644 -</para> -</programlisting> - -<para>The other possibility with permissions is that you might get a -completely empty window when you try to load the script. In this -case, it's likely that your access permissions are \emph{too - permissive}. Apache's <literal>suexec</literal> subsystem won't execute a -script that's group- or world-writable, for example. -</para> - -<para>Your web server may be configured to disallow execution of CGI -programs in your per-user web directory. Here's Apache's -default per-user configuration from my Fedora system. -</para> -<programlisting> -<para> <Directory /home/*/public_html> - AllowOverride FileInfo AuthConfig Limit - Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec - <Limit GET POST OPTIONS> - Order allow,deny - Allow from all - </Limit> - <LimitExcept GET POST OPTIONS> - Order deny,allow - Deny from all - </LimitExcept> - </Directory> -</para> -</programlisting> -<para>If you find a similar-looking <literal>Directory</literal> group in your Apache -configuration, the directive to look at inside it is <literal>Options</literal>. -Add <literal>ExecCGI</literal> to the end of this list if it's missing, and -restart the web server. -</para> - -<para>If you find that Apache serves you the text of the CGI script instead -of executing it, you may need to either uncomment (if already present) -or add a directive like this. -</para> -<programlisting> -<para> AddHandler cgi-script .cgi -</para> -</programlisting> - -<para>The next possibility is that you might be served with a colourful -Python backtrace claiming that it can't import a -<literal>mercurial</literal>-related module. This is actually progress! The -server is now capable of executing your CGI script. This error is -only likely to occur if you're running a private installation of -Mercurial, instead of a system-wide version. Remember that the web -server runs the CGI program without any of the environment variables -that you take for granted in an interactive session. If this error -happens to you, edit your copy of <filename role="special">hgweb.cgi</filename> and follow the -directions inside it to correctly set your <envar>PYTHONPATH</envar> -environment variable. -</para> - -<para>Finally, you are <emphasis>certain</emphasis> to by served with another colourful -Python backtrace: this one will complain that it can't find -<filename class="directory">/path/to/repository</filename>. Edit your <filename role="special">hgweb.cgi</filename> script -and replace the <filename class="directory">/path/to/repository</filename> string with the complete -path to the repository you want to serve up. -</para> - -<para>At this point, when you try to reload the page, you should be -presented with a nice HTML view of your repository's history. Whew! -</para> - -</sect3> -<sect3> -<title>Configuring lighttpd</title> - -<para>To be exhaustive in my experiments, I tried configuring the -increasingly popular <literal>lighttpd</literal> web server to serve the same -repository as I described with Apache above. I had already overcome -all of the problems I outlined with Apache, many of which are not -server-specific. As a result, I was fairly sure that my file and -directory permissions were good, and that my <filename role="special">hgweb.cgi</filename> -script was properly edited. -</para> - -<para>Once I had Apache running, getting <literal>lighttpd</literal> to serve the -repository was a snap (in other words, even if you're trying to use -<literal>lighttpd</literal>, you should read the Apache section). I first had -to edit the <literal>mod_access</literal> section of its config file to enable -<literal>mod_cgi</literal> and <literal>mod_userdir</literal>, both of which were -disabled by default on my system. I then added a few lines to the end -of the config file, to configure these modules. -</para> -<programlisting> -<para> userdir.path = "public_html" - cgi.assign = ( ".cgi" => "" ) -</para> -</programlisting> -<para>With this done, <literal>lighttpd</literal> ran immediately for me. If I had -configured <literal>lighttpd</literal> before Apache, I'd almost certainly have -run into many of the same system-level configuration problems as I did -with Apache. However, I found <literal>lighttpd</literal> to be noticeably -easier to configure than Apache, even though I've used Apache for over -a decade, and this was my first exposure to <literal>lighttpd</literal>. -</para> - -</sect3> -</sect2> -<sect2> -<title>Sharing multiple repositories with one CGI script</title> - -<para>The <filename role="special">hgweb.cgi</filename> script only lets you publish a single -repository, which is an annoying restriction. If you want to publish -more than one without wracking yourself with multiple copies of the -same script, each with different names, a better choice is to use the -<filename role="special">hgwebdir.cgi</filename> script. -</para> - -<para>The procedure to configure <filename role="special">hgwebdir.cgi</filename> is only a little -more involved than for <filename role="special">hgweb.cgi</filename>. First, you must obtain -a copy of the script. If you don't have one handy, you can download a -copy from the master Mercurial repository at -<ulink url="http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi">http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi</ulink>. -</para> - -<para>You'll need to copy this script into your <filename class="directory">public_html</filename> -directory, and ensure that it's executable. -</para> -<programlisting> -<para> cp .../hgwebdir.cgi /public_html - chmod 755 /public_html /public_html/hgwebdir.cgi -</para> -</programlisting> -<para>With basic configuration out of the way, try to visit -<ulink url="http://myhostname/ myuser/hgwebdir.cgi">http://myhostname/ myuser/hgwebdir.cgi</ulink> in your browser. It -should display an empty list of repositories. If you get a blank -window or error message, try walking through the list of potential -problems in section <xref linkend="sec:collab:wtf"/>. -</para> - -<para>The <filename role="special">hgwebdir.cgi</filename> script relies on an external -configuration file. By default, it searches for a file named -<filename role="special">hgweb.config</filename> in the same directory as itself. You'll need -to create this file, and make it world-readable. The format of the -file is similar to a Windows <quote>ini</quote> file, as understood by Python's -<literal>ConfigParser</literal> <citation>web:configparser</citation> module. -</para> - -<para>The easiest way to configure <filename role="special">hgwebdir.cgi</filename> is with a -section named <literal>collections</literal>. This will automatically publish -<emphasis>every</emphasis> repository under the directories you name. The section -should look like this: -</para> -<programlisting> -<para> [collections] - /my/root = /my/root -</para> -</programlisting> -<para>Mercurial interprets this by looking at the directory name on the -<emphasis>right</emphasis> hand side of the <quote><literal>=</literal></quote> sign; finding -repositories in that directory hierarchy; and using the text on the -<emphasis>left</emphasis> to strip off matching text from the names it will actually -list in the web interface. The remaining component of a path after -this stripping has occurred is called a <quote>virtual path</quote>. -</para> - -<para>Given the example above, if we have a repository whose local path is -<filename class="directory">/my/root/this/repo</filename>, the CGI script will strip the leading -<filename class="directory">/my/root</filename> from the name, and publish the repository with a -virtual path of <filename class="directory">this/repo</filename>. If the base URL for our CGI -script is <ulink url="http://myhostname/ myuser/hgwebdir.cgi">http://myhostname/ myuser/hgwebdir.cgi</ulink>, the complete -URL for that repository will be -<ulink url="http://myhostname/ myuser/hgwebdir.cgi/this/repo">http://myhostname/ myuser/hgwebdir.cgi/this/repo</ulink>. -</para> - -<para>If we replace <filename class="directory">/my/root</filename> on the left hand side of this example -with <filename class="directory">/my</filename>, then <filename role="special">hgwebdir.cgi</filename> will only strip off -<filename class="directory">/my</filename> from the repository name, and will give us a virtual -path of <filename class="directory">root/this/repo</filename> instead of <filename class="directory">this/repo</filename>. -</para> - -<para>The <filename role="special">hgwebdir.cgi</filename> script will recursively search each -directory listed in the <literal>collections</literal> section of its -configuration file, but it will <literal>not</literal> recurse into the -repositories it finds. -</para> - -<para>The <literal>collections</literal> mechanism makes it easy to publish many -repositories in a <quote>fire and forget</quote> manner. You only need to set up -the CGI script and configuration file one time. Afterwards, you can -publish or unpublish a repository at any time by simply moving it -into, or out of, the directory hierarchy in which you've configured -<filename role="special">hgwebdir.cgi</filename> to look. -</para> - -<sect3> -<title>Explicitly specifying which repositories to publish</title> - -<para>In addition to the <literal>collections</literal> mechanism, the -<filename role="special">hgwebdir.cgi</filename> script allows you to publish a specific list -of repositories. To do so, create a <literal>paths</literal> section, with -contents of the following form. -</para> -<programlisting> -<para> [paths] - repo1 = /my/path/to/some/repo - repo2 = /some/path/to/another -</para> -</programlisting> -<para>In this case, the virtual path (the component that will appear in a -URL) is on the left hand side of each definition, while the path to -the repository is on the right. Notice that there does not need to be -any relationship between the virtual path you choose and the location -of a repository in your filesystem. -</para> - -<para>If you wish, you can use both the <literal>collections</literal> and -<literal>paths</literal> mechanisms simultaneously in a single configuration -file. -</para> - -<note> -<para> If multiple repositories have the same virtual path, - <filename role="special">hgwebdir.cgi</filename> will not report an error. Instead, it will - behave unpredictably. -</para> -</note> - -</sect3> -</sect2> -<sect2> -<title>Downloading source archives</title> - -<para>Mercurial's web interface lets users download an archive of any -revision. This archive will contain a snapshot of the working -directory as of that revision, but it will not contain a copy of the -repository data. -</para> - -<para>By default, this feature is not enabled. To enable it, you'll need to -add an <envar role="rc-item-web">allow_archive</envar> item to the <literal role="rc-web">web</literal> -section of your <filename role="special"> /.hgrc</filename>. -</para> - -</sect2> -<sect2> -<title>Web configuration options</title> - -<para>Mercurial's web interfaces (the <command role="hg-cmd">hg serve</command> command, and the -<filename role="special">hgweb.cgi</filename> and <filename role="special">hgwebdir.cgi</filename> scripts) have a -number of configuration options that you can set. These belong in a -section named <literal role="rc-web">web</literal>. -</para> -<itemizedlist> -<listitem><para><envar role="rc-item-web">allow_archive</envar>: Determines which (if any) archive - download mechanisms Mercurial supports. If you enable this - feature, users of the web interface will be able to download an - archive of whatever revision of a repository they are viewing. - To enable the archive feature, this item must take the form of a - sequence of words drawn from the list below. -</para> -</listitem><itemizedlist> -<listitem><para> \item <literal>bz2</literal>: A <command>tar</command> archive, compressed using - <literal>bzip2</literal> compression. This has the best compression ratio, - but uses the most CPU time on the server. - \item <literal>gz</literal>: A <command>tar</command> archive, compressed using - <literal>gzip</literal> compression. - \item <literal>zip</literal>: A <command>zip</command> archive, compressed using LZW - compression. This format has the worst compression ratio, but is - widely used in the Windows world. -</para> -</listitem></itemizedlist> -<para> If you provide an empty list, or don't have an - <envar role="rc-item-web">allow_archive</envar> entry at all, this feature will be - disabled. Here is an example of how to enable all three supported - formats. -</para> -<programlisting> -<para> [web] - allow_archive = bz2 gz zip -</para> -</programlisting> -<listitem><para><envar role="rc-item-web">allowpull</envar>: Boolean. Determines whether the web - interface allows remote users to <command role="hg-cmd">hg pull</command> and <command role="hg-cmd">hg clone</command> this - repository over HTTP. If set to <literal>no</literal> or <literal>false</literal>, only - the <quote>human-oriented</quote> portion of the web interface is available. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">contact</envar>: String. A free-form (but preferably - brief) string identifying the person or group in charge of the - repository. This often contains the name and email address of a - person or mailing list. It often makes sense to place this entry in - a repository's own <filename role="special">.hg/hgrc</filename> file, but it can make sense - to use in a global <filename role="special"> /.hgrc</filename>\ if every repository has a single - maintainer. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">maxchanges</envar>: Integer. The default maximum number - of changesets to display in a single page of output. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">maxfiles</envar>: Integer. The default maximum number - of modified files to display in a single page of output. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">stripes</envar>: Integer. If the web interface displays - alternating <quote>stripes</quote> to make it easier to visually align rows - when you are looking at a table, this number controls the number of - rows in each stripe. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">style</envar>: Controls the template Mercurial uses to - display the web interface. Mercurial ships with two web templates, - named <literal>default</literal> and <literal>gitweb</literal> (the latter is much more - visually attractive). You can also specify a custom template of - your own; see chapter <xref linkend="chap:template"/> for details. Here, you - can see how to enable the <literal>gitweb</literal> style. -</para> -</listitem><programlisting> -<listitem><para> [web] - style = gitweb -</para> -</listitem></programlisting> -</para> -</listitem> -<listitem><para><envar role="rc-item-web">templates</envar>: Path. The directory in which to search - for template files. By default, Mercurial searches in the directory - in which it was installed. -</para> -</listitem></itemizedlist> -<para>If you are using <filename role="special">hgwebdir.cgi</filename>, you can place a few -configuration items in a <literal role="rc-web">web</literal> section of the -<filename role="special">hgweb.config</filename> file instead of a <filename role="special"> /.hgrc</filename>\ file, for -convenience. These items are <envar role="rc-item-web">motd</envar> and -<envar role="rc-item-web">style</envar>. -</para> - -<sect3> -<title>Options specific to an individual repository</title> - -<para>A few <literal role="rc-web">web</literal> configuration items ought to be placed in a -repository's local <filename role="special">.hg/hgrc</filename>, rather than a user's or -global <filename role="special"> /.hgrc</filename>. -</para> -<itemizedlist> -<listitem><para><envar role="rc-item-web">description</envar>: String. A free-form (but preferably - brief) string that describes the contents or purpose of the - repository. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">name</envar>: String. The name to use for the repository - in the web interface. This overrides the default name, which is the - last component of the repository's path. -</para> -</listitem></itemizedlist> - -</sect3> -<sect3> -<title>Options specific to the <command role="hg-cmd">hg serve</command> command</title> - -<para>Some of the items in the <literal role="rc-web">web</literal> section of a <filename role="special"> /.hgrc</filename>\ file are -only for use with the <command role="hg-cmd">hg serve</command> command. -</para> -<itemizedlist> -<listitem><para><envar role="rc-item-web">accesslog</envar>: Path. The name of a file into which to - write an access log. By default, the <command role="hg-cmd">hg serve</command> command writes - this information to standard output, not to a file. Log entries are - written in the standard <quote>combined</quote> file format used by almost all - web servers. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">address</envar>: String. The local address on which the - server should listen for incoming connections. By default, the - server listens on all addresses. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">errorlog</envar>: Path. The name of a file into which to - write an error log. By default, the <command role="hg-cmd">hg serve</command> command writes this - information to standard error, not to a file. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">ipv6</envar>: Boolean. Whether to use the IPv6 protocol. - By default, IPv6 is not used. -</para> -</listitem> -<listitem><para><envar role="rc-item-web">port</envar>: Integer. The TCP port number on which the - server should listen. The default port number used is 8000. -</para> -</listitem></itemizedlist> - -<para>\subsubsection{Choosing the right <filename role="special"> /.hgrc</filename>\ file to add <literal role="rc-web">web</literal> - items to} -</para> - -<para>It is important to remember that a web server like Apache or -<literal>lighttpd</literal> will run under a user ID that is different to yours. -CGI scripts run by your server, such as <filename role="special">hgweb.cgi</filename>, will -usually also run under that user ID. -</para> - -<para>If you add <literal role="rc-web">web</literal> items to your own personal <filename role="special"> /.hgrc</filename>\ file, CGI -scripts won't read that <filename role="special"> /.hgrc</filename>\ file. Those settings will thus only -affect the behaviour of the <command role="hg-cmd">hg serve</command> command when you run it. To -cause CGI scripts to see your settings, either create a <filename role="special"> /.hgrc</filename>\ file in -the home directory of the user ID that runs your web server, or add -those settings to a system-wide <filename role="special"> /.hgrc</filename>\ file. -</para> - - -</sect3> -</sect2> -</sect1> + </sect2> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch07-filenames.xml --- a/fr/ch07-filenames.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch07-filenames.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,388 +1,451 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>File names and pattern matching</title> -<para>\label{chap:names}</para> - -<para>Mercurial provides mechanisms that let you work with file names in a -consistent and expressive way.</para> - -<sect1> -<title>Simple file naming</title> - -<para>Mercurial uses a unified piece of machinery <quote>under the hood</quote> to -handle file names. Every command behaves uniformly with respect to -file names. The way in which commands work with file names is as -follows.</para> - -<para>If you explicitly name real files on the command line, Mercurial works -with exactly those files, as you would expect. -<!-- &interaction.filenames.files; --></para> - -<para>When you provide a directory name, Mercurial will interpret this as -<quote>operate on every file in this directory and its subdirectories</quote>. -Mercurial traverses the files and subdirectories in a directory in -alphabetical order. When it encounters a subdirectory, it will -traverse that subdirectory before continuing with the current -directory. -<!-- &interaction.filenames.dirs; --></para> - -</sect1> -<sect1> -<title>Running commands without any file names</title> - -<para>Mercurial's commands that work with file names have useful default -behaviours when you invoke them without providing any file names or -patterns. What kind of behaviour you should expect depends on what -the command does. Here are a few rules of thumb you can use to -predict what a command is likely to do if you don't give it any names -to work with.</para> -<itemizedlist> -<listitem><para>Most commands will operate on the entire working directory. - This is what the <command role="hg-cmd">hg add</command> command does, for example.</para> -</listitem> -<listitem><para>If the command has effects that are difficult or impossible to - reverse, it will force you to explicitly provide at least one name - or pattern (see below). This protects you from accidentally - deleting files by running <command role="hg-cmd">hg remove</command> with no arguments, for - example.</para> -</listitem></itemizedlist> - -<para>It's easy to work around these default behaviours if they don't suit -you. If a command normally operates on the whole working directory, -you can invoke it on just the current directory and its subdirectories -by giving it the name <quote><filename class="directory">.</filename></quote>. -<!-- &interaction.filenames.wdir-subdir; --> -</para> - -<para>Along the same lines, some commands normally print file names relative -to the root of the repository, even if you're invoking them from a -subdirectory. Such a command will print file names relative to your -subdirectory if you give it explicit names. Here, we're going to run -<command role="hg-cmd">hg status</command> from a subdirectory, and get it to operate on the -entire working directory while printing file names relative to our -subdirectory, by passing it the output of the <command role="hg-cmd">hg root</command> command. -<!-- &interaction.filenames.wdir-relname; --> -</para> - -</sect1> -<sect1> -<title>Telling you what's going on</title> - -<para>The <command role="hg-cmd">hg add</command> example in the preceding section illustrates something -else that's helpful about Mercurial commands. If a command operates -on a file that you didn't name explicitly on the command line, it will -usually print the name of the file, so that you will not be surprised -what's going on. -</para> - -<para>The principle here is of <emphasis>least surprise</emphasis>. If you've exactly -named a file on the command line, there's no point in repeating it -back at you. If Mercurial is acting on a file <emphasis>implicitly</emphasis>, -because you provided no names, or a directory, or a pattern (see -below), it's safest to tell you what it's doing. -</para> - -<para>For commands that behave this way, you can silence them using the -<option role="hg-opt-global">-q</option> option. You can also get them to print the name of every -file, even those you've named explicitly, using the <option role="hg-opt-global">-v</option> -option. -</para> - -</sect1> -<sect1> -<title>Using patterns to identify files</title> - -<para>In addition to working with file and directory names, Mercurial lets -you use <emphasis>patterns</emphasis> to identify files. Mercurial's pattern -handling is expressive. -</para> - -<para>On Unix-like systems (Linux, MacOS, etc.), the job of matching file -names to patterns normally falls to the shell. On these systems, you -must explicitly tell Mercurial that a name is a pattern. On Windows, -the shell does not expand patterns, so Mercurial will automatically -identify names that are patterns, and expand them for you. -</para> - -<para>To provide a pattern in place of a regular name on the command line, -the mechanism is simple: -</para> -<programlisting> -<para> syntax:patternbody -</para> +<chapter id="chap:names"> + <?dbhtml filename="file-names-and-pattern-matching.html"?> + <title>File names and pattern matching</title> + + <para id="x_543">Mercurial provides mechanisms that let you work with file + names in a consistent and expressive way.</para> + + <sect1> + <title>Simple file naming</title> + + <para id="x_544">Mercurial uses a unified piece of machinery <quote>under the + hood</quote> to handle file names. Every command behaves + uniformly with respect to file names. The way in which commands + work with file names is as follows.</para> + + <para id="x_545">If you explicitly name real files on the command line, + Mercurial works with exactly those files, as you would expect. + &interaction.filenames.files;</para> + + <para id="x_546">When you provide a directory name, Mercurial will interpret + this as <quote>operate on every file in this directory and its + subdirectories</quote>. Mercurial traverses the files and + subdirectories in a directory in alphabetical order. When it + encounters a subdirectory, it will traverse that subdirectory + before continuing with the current directory.</para> + + &interaction.filenames.dirs; + </sect1> + + <sect1> + <title>Running commands without any file names</title> + + <para id="x_547">Mercurial's commands that work with file names have useful + default behaviors when you invoke them without providing any + file names or patterns. What kind of behavior you should + expect depends on what the command does. Here are a few rules + of thumb you can use to predict what a command is likely to do + if you don't give it any names to work with.</para> + <itemizedlist> + <listitem><para id="x_548">Most commands will operate on the entire working + directory. This is what the <command role="hg-cmd">hg + add</command> command does, for example.</para> + </listitem> + <listitem><para id="x_549">If the command has effects that are difficult or + impossible to reverse, it will force you to explicitly + provide at least one name or pattern (see below). This + protects you from accidentally deleting files by running + <command role="hg-cmd">hg remove</command> with no + arguments, for example.</para> + </listitem></itemizedlist> + + <para id="x_54a">It's easy to work around these default behaviors if they + don't suit you. If a command normally operates on the whole + working directory, you can invoke it on just the current + directory and its subdirectories by giving it the name + <quote><filename class="directory">.</filename></quote>.</para> + + &interaction.filenames.wdir-subdir; + + <para id="x_54b">Along the same lines, some commands normally print file + names relative to the root of the repository, even if you're + invoking them from a subdirectory. Such a command will print + file names relative to your subdirectory if you give it explicit + names. Here, we're going to run <command role="hg-cmd">hg + status</command> from a subdirectory, and get it to operate on + the entire working directory while printing file names relative + to our subdirectory, by passing it the output of the <command + role="hg-cmd">hg root</command> command.</para> + + &interaction.filenames.wdir-relname; + </sect1> + + <sect1> + <title>Telling you what's going on</title> + + <para id="x_54c">The <command role="hg-cmd">hg add</command> example in the + preceding section illustrates something else that's helpful + about Mercurial commands. If a command operates on a file that + you didn't name explicitly on the command line, it will usually + print the name of the file, so that you will not be surprised + what's going on.</para> + + <para id="x_54d">The principle here is of <emphasis>least + surprise</emphasis>. If you've exactly named a file on the + command line, there's no point in repeating it back at you. If + Mercurial is acting on a file <emphasis>implicitly</emphasis>, e.g. + because you provided no names, or a directory, or a pattern (see + below), it is safest to tell you what files it's operating on.</para> + + <para id="x_54e">For commands that behave this way, you can silence them + using the <option role="hg-opt-global">-q</option> option. You + can also get them to print the name of every file, even those + you've named explicitly, using the <option + role="hg-opt-global">-v</option> option.</para> + </sect1> + + <sect1> + <title>Using patterns to identify files</title> + + <para id="x_54f">In addition to working with file and directory names, + Mercurial lets you use <emphasis>patterns</emphasis> to identify + files. Mercurial's pattern handling is expressive.</para> + + <para id="x_550">On Unix-like systems (Linux, MacOS, etc.), the job of + matching file names to patterns normally falls to the shell. On + these systems, you must explicitly tell Mercurial that a name is + a pattern. On Windows, the shell does not expand patterns, so + Mercurial will automatically identify names that are patterns, + and expand them for you.</para> + + <para id="x_551">To provide a pattern in place of a regular name on the + command line, the mechanism is simple:</para> + <programlisting>syntax:patternbody</programlisting> + <para id="x_552">That is, a pattern is identified by a short text string that + says what kind of pattern this is, followed by a colon, followed + by the actual pattern.</para> + + <para id="x_553">Mercurial supports two kinds of pattern syntax. The most + frequently used is called <literal>glob</literal>; this is the + same kind of pattern matching used by the Unix shell, and should + be familiar to Windows command prompt users, too.</para> + + <para id="x_554">When Mercurial does automatic pattern matching on Windows, + it uses <literal>glob</literal> syntax. You can thus omit the + <quote><literal>glob:</literal></quote> prefix on Windows, but + it's safe to use it, too.</para> + + <para id="x_555">The <literal>re</literal> syntax is more powerful; it lets + you specify patterns using regular expressions, also known as + regexps.</para> + + <para id="x_556">By the way, in the examples that follow, notice that I'm + careful to wrap all of my patterns in quote characters, so that + they won't get expanded by the shell before Mercurial sees + them.</para> + + <sect2> + <title>Shell-style <literal>glob</literal> patterns</title> + + <para id="x_557">This is an overview of the kinds of patterns you can use + when you're matching on glob patterns.</para> + + <para id="x_558">The <quote><literal>*</literal></quote> character matches + any string, within a single directory.</para> + + &interaction.filenames.glob.star; + + <para id="x_559">The <quote><literal>**</literal></quote> pattern matches + any string, and crosses directory boundaries. It's not a + standard Unix glob token, but it's accepted by several popular + Unix shells, and is very useful.</para> + + &interaction.filenames.glob.starstar; + + <para id="x_55a">The <quote><literal>?</literal></quote> pattern matches + any single character.</para> + + &interaction.filenames.glob.question; + + <para id="x_55b">The <quote><literal>[</literal></quote> character begins a + <emphasis>character class</emphasis>. This matches any single + character within the class. The class ends with a + <quote><literal>]</literal></quote> character. A class may + contain multiple <emphasis>range</emphasis>s of the form + <quote><literal>a-f</literal></quote>, which is shorthand for + <quote><literal>abcdef</literal></quote>.</para> + + &interaction.filenames.glob.range; + + <para id="x_55c">If the first character after the + <quote><literal>[</literal></quote> in a character class is a + <quote><literal>!</literal></quote>, it + <emphasis>negates</emphasis> the class, making it match any + single character not in the class.</para> + + <para id="x_55d">A <quote><literal>{</literal></quote> begins a group of + subpatterns, where the whole group matches if any subpattern + in the group matches. The <quote><literal>,</literal></quote> + character separates subpatterns, and + <quote><literal>}</literal></quote> ends the group.</para> + + &interaction.filenames.glob.group; + + <sect3> + <title>Watch out!</title> + + <para id="x_55e">Don't forget that if you want to match a pattern in any + directory, you should not be using the + <quote><literal>*</literal></quote> match-any token, as this + will only match within one directory. Instead, use the + <quote><literal>**</literal></quote> token. This small + example illustrates the difference between the two.</para> + + &interaction.filenames.glob.star-starstar; + </sect3> + </sect2> + + <sect2> + <title>Regular expression matching with <literal>re</literal> + patterns</title> + + <para id="x_55f">Mercurial accepts the same regular expression syntax as + the Python programming language (it uses Python's regexp + engine internally). This is based on the Perl language's + regexp syntax, which is the most popular dialect in use (it's + also used in Java, for example).</para> + + <para id="x_560">I won't discuss Mercurial's regexp dialect in any detail + here, as regexps are not often used. Perl-style regexps are + in any case already exhaustively documented on a multitude of + web sites, and in many books. Instead, I will focus here on a + few things you should know if you find yourself needing to use + regexps with Mercurial.</para> + + <para id="x_561">A regexp is matched against an entire file name, relative + to the root of the repository. In other words, even if you're + already in subbdirectory <filename + class="directory">foo</filename>, if you want to match files + under this directory, your pattern must start with + <quote><literal>foo/</literal></quote>.</para> + + <para id="x_562">One thing to note, if you're familiar with Perl-style + regexps, is that Mercurial's are <emphasis>rooted</emphasis>. + That is, a regexp starts matching against the beginning of a + string; it doesn't look for a match anywhere within the + string. To match anywhere in a string, start your pattern + with <quote><literal>.*</literal></quote>.</para> + </sect2> + </sect1> + + <sect1> + <title>Filtering files</title> + + <para id="x_563">Not only does Mercurial give you a variety of ways to + specify files; it lets you further winnow those files using + <emphasis>filters</emphasis>. Commands that work with file + names accept two filtering options.</para> + <itemizedlist> + <listitem><para id="x_564"><option role="hg-opt-global">-I</option>, or + <option role="hg-opt-global">--include</option>, lets you + specify a pattern that file names must match in order to be + processed.</para> + </listitem> + <listitem><para id="x_565"><option role="hg-opt-global">-X</option>, or + <option role="hg-opt-global">--exclude</option>, gives you a + way to <emphasis>avoid</emphasis> processing files, if they + match this pattern.</para> + </listitem></itemizedlist> + <para id="x_566">You can provide multiple <option + role="hg-opt-global">-I</option> and <option + role="hg-opt-global">-X</option> options on the command line, + and intermix them as you please. Mercurial interprets the + patterns you provide using glob syntax by default (but you can + use regexps if you need to).</para> + + <para id="x_567">You can read a <option role="hg-opt-global">-I</option> + filter as <quote>process only the files that match this + filter</quote>.</para> + + &interaction.filenames.filter.include; + + <para id="x_568">The <option role="hg-opt-global">-X</option> filter is best + read as <quote>process only the files that don't match this + pattern</quote>.</para> + + &interaction.filenames.filter.exclude; + </sect1> + + <sect1> + <title>Permanently ignoring unwanted files and directories</title> + + <para id="x_569">When you create a new repository, the chances are + that over time it will grow to contain files that ought to + <emphasis>not</emphasis> be managed by Mercurial, but which you + don't want to see listed every time you run <command>hg + status</command>. For instance, <quote>build products</quote> + are files that are created as part of a build but which should + not be managed by a revision control system. The most common + build products are output files produced by software tools such + as compilers. As another example, many text editors litter a + directory with lock files, temporary working files, and backup + files, which it also makes no sense to manage.</para> + + <para id="x_6b4">To have Mercurial permanently ignore such files, create a + file named <filename>.hgignore</filename> in the root of your + repository. You <emphasis>should</emphasis> <command>hg + add</command> this file so that it gets tracked with the rest of + your repository contents, since your collaborators will probably + find it useful too.</para> + + <para id="x_6b5">By default, the <filename>.hgignore</filename> file should + contain a list of regular expressions, one per line. Empty + lines are skipped. Most people prefer to describe the files they + want to ignore using the <quote>glob</quote> syntax that we + described above, so a typical <filename>.hgignore</filename> + file will start with this directive:</para> + + <programlisting>syntax: glob</programlisting> + + <para id="x_6b6">This tells Mercurial to interpret the lines that follow as + glob patterns, not regular expressions.</para> + + <para id="x_6b7">Here is a typical-looking <filename>.hgignore</filename> + file.</para> + + <programlisting>syntax: glob +# This line is a comment, and will be skipped. +# Empty lines are skipped too. + +# Backup files left behind by the Emacs editor. +*~ + +# Lock files used by the Emacs editor. +# Notice that the "#" character is quoted with a backslash. +# This prevents it from being interpreted as starting a comment. +.\#* + +# Temporary files used by the vim editor. +.*.swp + +# A hidden file created by the Mac OS X Finder. +.DS_Store </programlisting> -<para>That is, a pattern is identified by a short text string that says what -kind of pattern this is, followed by a colon, followed by the actual -pattern. -</para> - -<para>Mercurial supports two kinds of pattern syntax. The most frequently -used is called <literal>glob</literal>; this is the same kind of pattern -matching used by the Unix shell, and should be familiar to Windows -command prompt users, too. -</para> - -<para>When Mercurial does automatic pattern matching on Windows, it uses -<literal>glob</literal> syntax. You can thus omit the <quote><literal>glob:</literal></quote> prefix -on Windows, but it's safe to use it, too. -</para> - -<para>The <literal>re</literal> syntax is more powerful; it lets you specify patterns -using regular expressions, also known as regexps. -</para> - -<para>By the way, in the examples that follow, notice that I'm careful to -wrap all of my patterns in quote characters, so that they won't get -expanded by the shell before Mercurial sees them. -</para> - -<sect2> -<title>Shell-style <literal>glob</literal> patterns</title> - -<para>This is an overview of the kinds of patterns you can use when you're -matching on glob patterns. -</para> - -<para>The <quote><literal>*</literal></quote> character matches any string, within a single -directory. -<!-- &interaction.filenames.glob.star; --> -</para> - -<para>The <quote><literal>**</literal></quote> pattern matches any string, and crosses directory -boundaries. It's not a standard Unix glob token, but it's accepted by -several popular Unix shells, and is very useful. -<!-- &interaction.filenames.glob.starstar; --> -</para> - -<para>The <quote><literal>?</literal></quote> pattern matches any single character. -<!-- &interaction.filenames.glob.question; --> -</para> - -<para>The <quote><literal>[</literal></quote> character begins a <emphasis>character class</emphasis>. This -matches any single character within the class. The class ends with a -<quote><literal>]</literal></quote> character. A class may contain multiple <emphasis>range</emphasis>s -of the form <quote><literal>a-f</literal></quote>, which is shorthand for -<quote><literal>abcdef</literal></quote>. -<!-- &interaction.filenames.glob.range; --> -If the first character after the <quote><literal>[</literal></quote> in a character class -is a <quote><literal>!</literal></quote>, it <emphasis>negates</emphasis> the class, making it match any -single character not in the class. -</para> - -<para>A <quote><literal>{</literal></quote> begins a group of subpatterns, where the whole group -matches if any subpattern in the group matches. The <quote><literal>,</literal></quote> -character separates subpatterns, and <quote>\texttt{}}</quote> ends the group. -<!-- &interaction.filenames.glob.group; --> -</para> - -<sect3> -<title>Watch out!</title> - -<para>Don't forget that if you want to match a pattern in any directory, you -should not be using the <quote><literal>*</literal></quote> match-any token, as this will -only match within one directory. Instead, use the <quote><literal>**</literal></quote> -token. This small example illustrates the difference between the two. -<!-- &interaction.filenames.glob.star-starstar; --> -</para> - -</sect3> -</sect2> -<sect2> -<title>Regular expression matching with <literal>re</literal> patterns</title> - -<para>Mercurial accepts the same regular expression syntax as the Python -programming language (it uses Python's regexp engine internally). -This is based on the Perl language's regexp syntax, which is the most -popular dialect in use (it's also used in Java, for example). -</para> - -<para>I won't discuss Mercurial's regexp dialect in any detail here, as -regexps are not often used. Perl-style regexps are in any case -already exhaustively documented on a multitude of web sites, and in -many books. Instead, I will focus here on a few things you should -know if you find yourself needing to use regexps with Mercurial. -</para> - -<para>A regexp is matched against an entire file name, relative to the root -of the repository. In other words, even if you're already in -subbdirectory <filename class="directory">foo</filename>, if you want to match files under this -directory, your pattern must start with <quote><literal>foo/</literal></quote>. -</para> - -<para>One thing to note, if you're familiar with Perl-style regexps, is that -Mercurial's are <emphasis>rooted</emphasis>. That is, a regexp starts matching -against the beginning of a string; it doesn't look for a match -anywhere within the string. To match anywhere in a string, start -your pattern with <quote><literal>.*</literal></quote>. -</para> - -</sect2> -</sect1> -<sect1> -<title>Filtering files</title> - -<para>Not only does Mercurial give you a variety of ways to specify files; -it lets you further winnow those files using <emphasis>filters</emphasis>. Commands -that work with file names accept two filtering options. -</para> -<itemizedlist> -<listitem><para><option role="hg-opt-global">-I</option>, or <option role="hg-opt-global">--include</option>, lets you specify a pattern - that file names must match in order to be processed. -</para> -</listitem> -<listitem><para><option role="hg-opt-global">-X</option>, or <option role="hg-opt-global">--exclude</option>, gives you a way to - <emphasis>avoid</emphasis> processing files, if they match this pattern. -</para> -</listitem></itemizedlist> -<para>You can provide multiple <option role="hg-opt-global">-I</option> and <option role="hg-opt-global">-X</option> options on the -command line, and intermix them as you please. Mercurial interprets -the patterns you provide using glob syntax by default (but you can use -regexps if you need to). -</para> - -<para>You can read a <option role="hg-opt-global">-I</option> filter as <quote>process only the files that -match this filter</quote>. -<!-- &interaction.filenames.filter.include; --> -The <option role="hg-opt-global">-X</option> filter is best read as <quote>process only the files that -don't match this pattern</quote>. -<!-- &interaction.filenames.filter.exclude; --> -</para> - -</sect1> -<sect1> -<title>Ignoring unwanted files and directories</title> - -<para>XXX. -</para> - -</sect1> -<sect1> -<title>Case sensitivity</title> -<para>\label{sec:names:case} -</para> - -<para>If you're working in a mixed development environment that contains -both Linux (or other Unix) systems and Macs or Windows systems, you -should keep in the back of your mind the knowledge that they treat the -case (<quote>N</quote> versus <quote>n</quote>) of file names in incompatible ways. This is -not very likely to affect you, and it's easy to deal with if it does, -but it could surprise you if you don't know about it. -</para> - -<para>Operating systems and filesystems differ in the way they handle the -<emphasis>case</emphasis> of characters in file and directory names. There are -three common ways to handle case in names. -</para> -<itemizedlist> -<listitem><para>Completely case insensitive. Uppercase and lowercase versions - of a letter are treated as identical, both when creating a file and - during subsequent accesses. This is common on older DOS-based - systems. -</para> -</listitem> -<listitem><para>Case preserving, but insensitive. When a file or directory is - created, the case of its name is stored, and can be retrieved and - displayed by the operating system. When an existing file is being - looked up, its case is ignored. This is the standard arrangement on - Windows and MacOS. The names <filename>foo</filename> and <filename>FoO</filename> - identify the same file. This treatment of uppercase and lowercase - letters as interchangeable is also referred to as \emph{case - folding}. -</para> -</listitem> -<listitem><para>Case sensitive. The case of a name is significant at all times. - The names <filename>foo</filename> and {FoO} identify different files. This - is the way Linux and Unix systems normally work. -</para> -</listitem></itemizedlist> - -<para>On Unix-like systems, it is possible to have any or all of the above -ways of handling case in action at once. For example, if you use a -USB thumb drive formatted with a FAT32 filesystem on a Linux system, -Linux will handle names on that filesystem in a case preserving, but -insensitive, way. -</para> - -<sect2> -<title>Safe, portable repository storage</title> - -<para>Mercurial's repository storage mechanism is <emphasis>case safe</emphasis>. It -translates file names so that they can be safely stored on both case -sensitive and case insensitive filesystems. This means that you can -use normal file copying tools to transfer a Mercurial repository onto, -for example, a USB thumb drive, and safely move that drive and -repository back and forth between a Mac, a PC running Windows, and a -Linux box. -</para> - -</sect2> -<sect2> -<title>Detecting case conflicts</title> - -<para>When operating in the working directory, Mercurial honours the naming -policy of the filesystem where the working directory is located. If -the filesystem is case preserving, but insensitive, Mercurial will -treat names that differ only in case as the same. -</para> - -<para>An important aspect of this approach is that it is possible to commit -a changeset on a case sensitive (typically Linux or Unix) filesystem -that will cause trouble for users on case insensitive (usually Windows -and MacOS) users. If a Linux user commits changes to two files, one -named <filename>myfile.c</filename> and the other named <filename>MyFile.C</filename>, -they will be stored correctly in the repository. And in the working -directories of other Linux users, they will be correctly represented -as separate files. -</para> - -<para>If a Windows or Mac user pulls this change, they will not initially -have a problem, because Mercurial's repository storage mechanism is -case safe. However, once they try to <command role="hg-cmd">hg update</command> the working -directory to that changeset, or <command role="hg-cmd">hg merge</command> with that changeset, -Mercurial will spot the conflict between the two file names that the -filesystem would treat as the same, and forbid the update or merge -from occurring. -</para> - -</sect2> -<sect2> -<title>Fixing a case conflict</title> - -<para>If you are using Windows or a Mac in a mixed environment where some of -your collaborators are using Linux or Unix, and Mercurial reports a -case folding conflict when you try to <command role="hg-cmd">hg update</command> or <command role="hg-cmd">hg merge</command>, -the procedure to fix the problem is simple. -</para> - -<para>Just find a nearby Linux or Unix box, clone the problem repository -onto it, and use Mercurial's <command role="hg-cmd">hg rename</command> command to change the -names of any offending files or directories so that they will no -longer cause case folding conflicts. Commit this change, <command role="hg-cmd">hg pull</command> -or <command role="hg-cmd">hg push</command> it across to your Windows or MacOS system, and -<command role="hg-cmd">hg update</command> to the revision with the non-conflicting names. -</para> - -<para>The changeset with case-conflicting names will remain in your -project's history, and you still won't be able to <command role="hg-cmd">hg update</command> your -working directory to that changeset on a Windows or MacOS system, but -you can continue development unimpeded. -</para> - -<note> -<para> Prior to version 0.9.3, Mercurial did not use a case safe repository - storage mechanism, and did not detect case folding conflicts. If - you are using an older version of Mercurial on Windows or MacOS, I - strongly recommend that you upgrade. -</para> -</note> - -</sect2> -</sect1> + </sect1> + + <sect1 id="sec:names:case"> + <title>Case sensitivity</title> + + <para id="x_56a">If you're working in a mixed development environment that + contains both Linux (or other Unix) systems and Macs or Windows + systems, you should keep in the back of your mind the knowledge + that they treat the case (<quote>N</quote> versus + <quote>n</quote>) of file names in incompatible ways. This is + not very likely to affect you, and it's easy to deal with if it + does, but it could surprise you if you don't know about + it.</para> + + <para id="x_56b">Operating systems and filesystems differ in the way they + handle the <emphasis>case</emphasis> of characters in file and + directory names. There are three common ways to handle case in + names.</para> + <itemizedlist> + <listitem><para id="x_56c">Completely case insensitive. Uppercase and + lowercase versions of a letter are treated as identical, + both when creating a file and during subsequent accesses. + This is common on older DOS-based systems.</para> + </listitem> + <listitem><para id="x_56d">Case preserving, but insensitive. When a file + or directory is created, the case of its name is stored, and + can be retrieved and displayed by the operating system. + When an existing file is being looked up, its case is + ignored. This is the standard arrangement on Windows and + MacOS. The names <filename>foo</filename> and + <filename>FoO</filename> identify the same file. This + treatment of uppercase and lowercase letters as + interchangeable is also referred to as <emphasis>case + folding</emphasis>.</para> + </listitem> + <listitem><para id="x_56e">Case sensitive. The case of a name + is significant at all times. The names + <filename>foo</filename> and <filename>FoO</filename> + identify different files. This is the way Linux and Unix + systems normally work.</para> + </listitem></itemizedlist> + + <para id="x_56f">On Unix-like systems, it is possible to have any or all of + the above ways of handling case in action at once. For example, + if you use a USB thumb drive formatted with a FAT32 filesystem + on a Linux system, Linux will handle names on that filesystem in + a case preserving, but insensitive, way.</para> + + <sect2> + <title>Safe, portable repository storage</title> + + <para id="x_570">Mercurial's repository storage mechanism is <emphasis>case + safe</emphasis>. It translates file names so that they can + be safely stored on both case sensitive and case insensitive + filesystems. This means that you can use normal file copying + tools to transfer a Mercurial repository onto, for example, a + USB thumb drive, and safely move that drive and repository + back and forth between a Mac, a PC running Windows, and a + Linux box.</para> + + </sect2> + <sect2> + <title>Detecting case conflicts</title> + + <para id="x_571">When operating in the working directory, Mercurial honours + the naming policy of the filesystem where the working + directory is located. If the filesystem is case preserving, + but insensitive, Mercurial will treat names that differ only + in case as the same.</para> + + <para id="x_572">An important aspect of this approach is that it is + possible to commit a changeset on a case sensitive (typically + Linux or Unix) filesystem that will cause trouble for users on + case insensitive (usually Windows and MacOS) users. If a + Linux user commits changes to two files, one named + <filename>myfile.c</filename> and the other named + <filename>MyFile.C</filename>, they will be stored correctly + in the repository. And in the working directories of other + Linux users, they will be correctly represented as separate + files.</para> + + <para id="x_573">If a Windows or Mac user pulls this change, they will not + initially have a problem, because Mercurial's repository + storage mechanism is case safe. However, once they try to + <command role="hg-cmd">hg update</command> the working + directory to that changeset, or <command role="hg-cmd">hg + merge</command> with that changeset, Mercurial will spot the + conflict between the two file names that the filesystem would + treat as the same, and forbid the update or merge from + occurring.</para> + </sect2> + + <sect2> + <title>Fixing a case conflict</title> + + <para id="x_574">If you are using Windows or a Mac in a mixed environment + where some of your collaborators are using Linux or Unix, and + Mercurial reports a case folding conflict when you try to + <command role="hg-cmd">hg update</command> or <command + role="hg-cmd">hg merge</command>, the procedure to fix the + problem is simple.</para> + + <para id="x_575">Just find a nearby Linux or Unix box, clone the problem + repository onto it, and use Mercurial's <command + role="hg-cmd">hg rename</command> command to change the + names of any offending files or directories so that they will + no longer cause case folding conflicts. Commit this change, + <command role="hg-cmd">hg pull</command> or <command + role="hg-cmd">hg push</command> it across to your Windows or + MacOS system, and <command role="hg-cmd">hg update</command> + to the revision with the non-conflicting names.</para> + + <para id="x_576">The changeset with case-conflicting names will remain in + your project's history, and you still won't be able to + <command role="hg-cmd">hg update</command> your working + directory to that changeset on a Windows or MacOS system, but + you can continue development unimpeded.</para> + </sect2> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch08-branch.xml --- a/fr/ch08-branch.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch08-branch.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,473 +1,533 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Managing releases and branchy development</title> -<para>\label{chap:branch}</para> - -<para>Mercurial provides several mechanisms for you to manage a project that -is making progress on multiple fronts at once. To understand these -mechanisms, let's first take a brief look at a fairly normal software -project structure.</para> - -<para>Many software projects issue periodic <quote>major</quote> releases that contain -substantial new features. In parallel, they may issue <quote>minor</quote> -releases. These are usually identical to the major releases off which -they're based, but with a few bugs fixed.</para> - -<para>In this chapter, we'll start by talking about how to keep records of -project milestones such as releases. We'll then continue on to talk -about the flow of work between different phases of a project, and how -Mercurial can help you to isolate and manage this work.</para> - -<sect1> -<title>Giving a persistent name to a revision</title> - -<para>Once you decide that you'd like to call a particular revision a -<quote>release</quote>, it's a good idea to record the identity of that revision. -This will let you reproduce that release at a later date, for whatever -purpose you might need at the time (reproducing a bug, porting to a -new platform, etc). -<!-- &interaction.tag.init; --></para> - -<para>Mercurial lets you give a permanent name to any revision using the -<command role="hg-cmd">hg tag</command> command. Not surprisingly, these names are called -<quote>tags</quote>. -<!-- &interaction.tag.tag; --></para> - -<para>A tag is nothing more than a <quote>symbolic name</quote> for a revision. Tags -exist purely for your convenience, so that you have a handy permanent -way to refer to a revision; Mercurial doesn't interpret the tag names -you use in any way. Neither does Mercurial place any restrictions on -the name of a tag, beyond a few that are necessary to ensure that a -tag can be parsed unambiguously. A tag name cannot contain any of the -following characters:</para> -<itemizedlist> -<listitem><para>Colon (ASCII 58, <quote><literal>:</literal></quote>)</para> -</listitem> -<listitem><para>Carriage return (ASCII 13, <quote><literal>\r</literal></quote>) -</para> -</listitem> -<listitem><para>Newline (ASCII 10, <quote><literal>\n</literal></quote>) -</para> -</listitem></itemizedlist> - -<para>You can use the <command role="hg-cmd">hg tags</command> command to display the tags present in -your repository. In the output, each tagged revision is identified -first by its name, then by revision number, and finally by the unique -hash of the revision. -<!-- &interaction.tag.tags; --> -Notice that <literal>tip</literal> is listed in the output of <command role="hg-cmd">hg tags</command>. The -<literal>tip</literal> tag is a special <quote>floating</quote> tag, which always -identifies the newest revision in the repository. -</para> - -<para>In the output of the <command role="hg-cmd">hg tags</command> command, tags are listed in reverse -order, by revision number. This usually means that recent tags are -listed before older tags. It also means that <literal>tip</literal> is always -going to be the first tag listed in the output of <command role="hg-cmd">hg tags</command>. -</para> - -<para>When you run <command role="hg-cmd">hg log</command>, if it displays a revision that has tags -associated with it, it will print those tags. -<!-- &interaction.tag.log; --> -</para> - -<para>Any time you need to provide a revision ID to a Mercurial command, the -command will accept a tag name in its place. Internally, Mercurial -will translate your tag name into the corresponding revision ID, then -use that. -<!-- &interaction.tag.log.v1.0; --> -</para> - -<para>There's no limit on the number of tags you can have in a repository, -or on the number of tags that a single revision can have. As a -practical matter, it's not a great idea to have <quote>too many</quote> (a number -which will vary from project to project), simply because tags are -supposed to help you to find revisions. If you have lots of tags, the -ease of using them to identify revisions diminishes rapidly. -</para> - -<para>For example, if your project has milestones as frequent as every few -days, it's perfectly reasonable to tag each one of those. But if you -have a continuous build system that makes sure every revision can be -built cleanly, you'd be introducing a lot of noise if you were to tag -every clean build. Instead, you could tag failed builds (on the -assumption that they're rare!), or simply not use tags to track -buildability. -</para> - -<para>If you want to remove a tag that you no longer want, use -<command role="hg-cmd">hg tag --remove</command>. -<!-- &interaction.tag.remove; --> -You can also modify a tag at any time, so that it identifies a -different revision, by simply issuing a new <command role="hg-cmd">hg tag</command> command. -You'll have to use the <option role="hg-opt-tag">-f</option> option to tell Mercurial that -you <emphasis>really</emphasis> want to update the tag. -<!-- &interaction.tag.replace; --> -There will still be a permanent record of the previous identity of the -tag, but Mercurial will no longer use it. There's thus no penalty to -tagging the wrong revision; all you have to do is turn around and tag -the correct revision once you discover your error. -</para> - -<para>Mercurial stores tags in a normal revision-controlled file in your -repository. If you've created any tags, you'll find them in a file -named <filename role="special">.hgtags</filename>. When you run the <command role="hg-cmd">hg tag</command> command, -Mercurial modifies this file, then automatically commits the change to -it. This means that every time you run <command role="hg-cmd">hg tag</command>, you'll see a -corresponding changeset in the output of <command role="hg-cmd">hg log</command>. -<!-- &interaction.tag.tip; --> -</para> - -<sect2> -<title>Handling tag conflicts during a merge</title> - -<para>You won't often need to care about the <filename role="special">.hgtags</filename> file, but -it sometimes makes its presence known during a merge. The format of -the file is simple: it consists of a series of lines. Each line -starts with a changeset hash, followed by a space, followed by the -name of a tag. -</para> - -<para>If you're resolving a conflict in the <filename role="special">.hgtags</filename> file during -a merge, there's one twist to modifying the <filename role="special">.hgtags</filename> file: -when Mercurial is parsing the tags in a repository, it <emphasis>never</emphasis> -reads the working copy of the <filename role="special">.hgtags</filename> file. Instead, it -reads the <emphasis>most recently committed</emphasis> revision of the file. -</para> - -<para>An unfortunate consequence of this design is that you can't actually -verify that your merged <filename role="special">.hgtags</filename> file is correct until -<emphasis>after</emphasis> you've committed a change. So if you find yourself -resolving a conflict on <filename role="special">.hgtags</filename> during a merge, be sure to -run <command role="hg-cmd">hg tags</command> after you commit. If it finds an error in the -<filename role="special">.hgtags</filename> file, it will report the location of the error, -which you can then fix and commit. You should then run <command role="hg-cmd">hg tags</command> -again, just to be sure that your fix is correct. -</para> - -</sect2> -<sect2> -<title>Tags and cloning</title> - -<para>You may have noticed that the <command role="hg-cmd">hg clone</command> command has a -<option role="hg-opt-clone">-r</option> option that lets you clone an exact copy of the -repository as of a particular changeset. The new clone will not -contain any project history that comes after the revision you -specified. This has an interaction with tags that can surprise the -unwary. -</para> - -<para>Recall that a tag is stored as a revision to the <filename role="special">.hgtags</filename> -file, so that when you create a tag, the changeset in which it's -recorded necessarily refers to an older changeset. When you run -<command role="hg-cmd">hg clone -r foo</command> to clone a repository as of tag -<literal>foo</literal>, the new clone \emph{will not contain the history that - created the tag} that you used to clone the repository. The result -is that you'll get exactly the right subset of the project's history -in the new repository, but <emphasis>not</emphasis> the tag you might have expected. -</para> - -</sect2> -<sect2> -<title>When permanent tags are too much</title> - -<para>Since Mercurial's tags are revision controlled and carried around with -a project's history, everyone you work with will see the tags you -create. But giving names to revisions has uses beyond simply noting -that revision <literal>4237e45506ee</literal> is really <literal>v2.0.2</literal>. If -you're trying to track down a subtle bug, you might want a tag to -remind you of something like <quote>Anne saw the symptoms with this -revision</quote>. -</para> - -<para>For cases like this, what you might want to use are <emphasis>local</emphasis> tags. -You can create a local tag with the <option role="hg-opt-tag">-l</option> option to the -<command role="hg-cmd">hg tag</command> command. This will store the tag in a file called -<filename role="special">.hg/localtags</filename>. Unlike <filename role="special">.hgtags</filename>, -<filename role="special">.hg/localtags</filename> is not revision controlled. Any tags you -create using <option role="hg-opt-tag">-l</option> remain strictly local to the repository -you're currently working in. -</para> - -</sect2> -</sect1> -<sect1> -<title>The flow of changes&emdash;big picture vs. little</title> - -<para>To return to the outline I sketched at the beginning of a chapter, -let's think about a project that has multiple concurrent pieces of -work under development at once. -</para> - -<para>There might be a push for a new <quote>main</quote> release; a new minor bugfix -release to the last main release; and an unexpected <quote>hot fix</quote> to an -old release that is now in maintenance mode. -</para> - -<para>The usual way people refer to these different concurrent directions of -development is as <quote>branches</quote>. However, we've already seen numerous -times that Mercurial treats <emphasis>all of history</emphasis> as a series of -branches and merges. Really, what we have here is two ideas that are -peripherally related, but which happen to share a name. -</para> -<itemizedlist> -<listitem><para><quote>Big picture</quote> branches represent the sweep of a project's - evolution; people give them names, and talk about them in - conversation. -</para> -</listitem> -<listitem><para><quote>Little picture</quote> branches are artefacts of the day-to-day - activity of developing and merging changes. They expose the - narrative of how the code was developed. -</para> -</listitem></itemizedlist> - -</sect1> -<sect1> -<title>Managing big-picture branches in repositories</title> - -<para>The easiest way to isolate a <quote>big picture</quote> branch in Mercurial is in -a dedicated repository. If you have an existing shared -repository&emdash;let's call it <literal>myproject</literal>&emdash;that reaches a <quote>1.0</quote> -milestone, you can start to prepare for future maintenance releases on -top of version 1.0 by tagging the revision from which you prepared -the 1.0 release. -<!-- &interaction.branch-repo.tag; --> -You can then clone a new shared <literal>myproject-1.0.1</literal> repository as -of that tag. -<!-- &interaction.branch-repo.clone; --> -</para> - -<para>Afterwards, if someone needs to work on a bug fix that ought to go -into an upcoming 1.0.1 minor release, they clone the -<literal>myproject-1.0.1</literal> repository, make their changes, and push them -back. -<!-- &interaction.branch-repo.bugfix; --> -Meanwhile, development for the next major release can continue, -isolated and unabated, in the <literal>myproject</literal> repository. -<!-- &interaction.branch-repo.new; --> -</para> - -</sect1> -<sect1> -<title>Don't repeat yourself: merging across branches</title> - -<para>In many cases, if you have a bug to fix on a maintenance branch, the -chances are good that the bug exists on your project's main branch -(and possibly other maintenance branches, too). It's a rare developer -who wants to fix the same bug multiple times, so let's look at a few -ways that Mercurial can help you to manage these bugfixes without -duplicating your work. -</para> - -<para>In the simplest instance, all you need to do is pull changes from your -maintenance branch into your local clone of the target branch. -<!-- &interaction.branch-repo.pull; --> -You'll then need to merge the heads of the two branches, and push back -to the main branch. -<!-- &interaction.branch-repo.merge; --> -</para> - -</sect1> -<sect1> -<title>Naming branches within one repository</title> - -<para>In most instances, isolating branches in repositories is the right -approach. Its simplicity makes it easy to understand; and so it's -hard to make mistakes. There's a one-to-one relationship between -branches you're working in and directories on your system. This lets -you use normal (non-Mercurial-aware) tools to work on files within a -branch/repository. -</para> - -<para>If you're more in the <quote>power user</quote> category (<emphasis>and</emphasis> your -collaborators are too), there is an alternative way of handling -branches that you can consider. I've already mentioned the -human-level distinction between <quote>small picture</quote> and <quote>big picture</quote> -branches. While Mercurial works with multiple <quote>small picture</quote> -branches in a repository all the time (for example after you pull -changes in, but before you merge them), it can <emphasis>also</emphasis> work with -multiple <quote>big picture</quote> branches. -</para> - -<para>The key to working this way is that Mercurial lets you assign a -persistent <emphasis>name</emphasis> to a branch. There always exists a branch -named <literal>default</literal>. Even before you start naming branches -yourself, you can find traces of the <literal>default</literal> branch if you -look for them. -</para> - -<para>As an example, when you run the <command role="hg-cmd">hg commit</command> command, and it pops up -your editor so that you can enter a commit message, look for a line -that contains the text <quote><literal>HG: branch default</literal></quote> at the bottom. -This is telling you that your commit will occur on the branch named -<literal>default</literal>. -</para> - -<para>To start working with named branches, use the <command role="hg-cmd">hg branches</command> -command. This command lists the named branches already present in -your repository, telling you which changeset is the tip of each. -<!-- &interaction.branch-named.branches; --> -Since you haven't created any named branches yet, the only one that -exists is <literal>default</literal>. -</para> - -<para>To find out what the <quote>current</quote> branch is, run the <command role="hg-cmd">hg branch</command> -command, giving it no arguments. This tells you what branch the -parent of the current changeset is on. -<!-- &interaction.branch-named.branch; --> -</para> - -<para>To create a new branch, run the <command role="hg-cmd">hg branch</command> command again. This -time, give it one argument: the name of the branch you want to create. -<!-- &interaction.branch-named.create; --> -</para> - -<para>After you've created a branch, you might wonder what effect the -<command role="hg-cmd">hg branch</command> command has had. What do the <command role="hg-cmd">hg status</command> and -<command role="hg-cmd">hg tip</command> commands report? -<!-- &interaction.branch-named.status; --> -Nothing has changed in the working directory, and there's been no new -history created. As this suggests, running the <command role="hg-cmd">hg branch</command> command -has no permanent effect; it only tells Mercurial what branch name to -use the <emphasis>next</emphasis> time you commit a changeset. -</para> - -<para>When you commit a change, Mercurial records the name of the branch on -which you committed. Once you've switched from the <literal>default</literal> -branch to another and committed, you'll see the name of the new branch -show up in the output of <command role="hg-cmd">hg log</command>, <command role="hg-cmd">hg tip</command>, and other commands -that display the same kind of output. -<!-- &interaction.branch-named.commit; --> -The <command role="hg-cmd">hg log</command>-like commands will print the branch name of every -changeset that's not on the <literal>default</literal> branch. As a result, if -you never use named branches, you'll never see this information. -</para> - -<para>Once you've named a branch and committed a change with that name, -every subsequent commit that descends from that change will inherit -the same branch name. You can change the name of a branch at any -time, using the <command role="hg-cmd">hg branch</command> command. -<!-- &interaction.branch-named.rebranch; --> -In practice, this is something you won't do very often, as branch -names tend to have fairly long lifetimes. (This isn't a rule, just an -observation.) -</para> - -</sect1> -<sect1> -<title>Dealing with multiple named branches in a repository</title> - -<para>If you have more than one named branch in a repository, Mercurial will -remember the branch that your working directory on when you start a -command like <command role="hg-cmd">hg update</command> or <command role="hg-cmd">hg pull -u</command>. It will update -the working directory to the tip of this branch, no matter what the -<quote>repo-wide</quote> tip is. To update to a revision that's on a different -named branch, you may need to use the <option role="hg-opt-update">-C</option> option to -<command role="hg-cmd">hg update</command>. -</para> - -<para>This behaviour is a little subtle, so let's see it in action. First, -let's remind ourselves what branch we're currently on, and what -branches are in our repository. -<!-- &interaction.branch-named.parents; --> -We're on the <literal>bar</literal> branch, but there also exists an older -<command role="hg-cmd">hg foo</command> branch. -</para> - -<para>We can <command role="hg-cmd">hg update</command> back and forth between the tips of the -<literal>foo</literal> and <literal>bar</literal> branches without needing to use the -<option role="hg-opt-update">-C</option> option, because this only involves going backwards -and forwards linearly through our change history. -<!-- &interaction.branch-named.update-switchy; --> -</para> - -<para>If we go back to the <literal>foo</literal> branch and then run <command role="hg-cmd">hg update</command>, -it will keep us on <literal>foo</literal>, not move us to the tip of -<literal>bar</literal>. -<!-- &interaction.branch-named.update-nothing; --> -</para> - -<para>Committing a new change on the <literal>foo</literal> branch introduces a new -head. -<!-- &interaction.branch-named.foo-commit; --> -</para> - -</sect1> -<sect1> -<title>Branch names and merging</title> - -<para>As you've probably noticed, merges in Mercurial are not symmetrical. -Let's say our repository has two heads, 17 and 23. If I -<command role="hg-cmd">hg update</command> to 17 and then <command role="hg-cmd">hg merge</command> with 23, Mercurial records -17 as the first parent of the merge, and 23 as the second. Whereas if -I <command role="hg-cmd">hg update</command> to 23 and then <command role="hg-cmd">hg merge</command> with 17, it records 23 -as the first parent, and 17 as the second. -</para> - -<para>This affects Mercurial's choice of branch name when you merge. After -a merge, Mercurial will retain the branch name of the first parent -when you commit the result of the merge. If your first parent's -branch name is <literal>foo</literal>, and you merge with <literal>bar</literal>, the -branch name will still be <literal>foo</literal> after you merge. -</para> - -<para>It's not unusual for a repository to contain multiple heads, each with -the same branch name. Let's say I'm working on the <literal>foo</literal> -branch, and so are you. We commit different changes; I pull your -changes; I now have two heads, each claiming to be on the <literal>foo</literal> -branch. The result of a merge will be a single head on the -<literal>foo</literal> branch, as you might hope. -</para> - -<para>But if I'm working on the <literal>bar</literal> branch, and I merge work from -the <literal>foo</literal> branch, the result will remain on the <literal>bar</literal> -branch. -<!-- &interaction.branch-named.merge; --> -</para> - -<para>To give a more concrete example, if I'm working on the -<literal>bleeding-edge</literal> branch, and I want to bring in the latest fixes -from the <literal>stable</literal> branch, Mercurial will choose the <quote>right</quote> -(<literal>bleeding-edge</literal>) branch name when I pull and merge from -<literal>stable</literal>. -</para> - -</sect1> -<sect1> -<title>Branch naming is generally useful</title> - -<para>You shouldn't think of named branches as applicable only to situations -where you have multiple long-lived branches cohabiting in a single -repository. They're very useful even in the one-branch-per-repository -case. -</para> - -<para>In the simplest case, giving a name to each branch gives you a -permanent record of which branch a changeset originated on. This -gives you more context when you're trying to follow the history of a -long-lived branchy project. -</para> - -<para>If you're working with shared repositories, you can set up a -<literal role="hook">pretxnchangegroup</literal> hook on each that will block incoming changes -that have the <quote>wrong</quote> branch name. This provides a simple, but -effective, defence against people accidentally pushing changes from a -<quote>bleeding edge</quote> branch to a <quote>stable</quote> branch. Such a hook might -look like this inside the shared repo's <filename role="special"> /.hgrc</filename>. -</para> -<programlisting> -<para> [hooks] - pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch -</para> -</programlisting> - -</sect1> +<chapter id="chap:branch"> + <?dbhtml filename="managing-releases-and-branchy-development.html"?> + <title>Managing releases and branchy development</title> + + <para id="x_369">Mercurial provides several mechanisms for you to manage a + project that is making progress on multiple fronts at once. To + understand these mechanisms, let's first take a brief look at a + fairly normal software project structure.</para> + + <para id="x_36a">Many software projects issue periodic <quote>major</quote> + releases that contain substantial new features. In parallel, they + may issue <quote>minor</quote> releases. These are usually + identical to the major releases off which they're based, but with + a few bugs fixed.</para> + + <para id="x_36b">In this chapter, we'll start by talking about how to keep + records of project milestones such as releases. We'll then + continue on to talk about the flow of work between different + phases of a project, and how Mercurial can help you to isolate and + manage this work.</para> + + <sect1> + <title>Giving a persistent name to a revision</title> + + <para id="x_36c">Once you decide that you'd like to call a particular + revision a <quote>release</quote>, it's a good idea to record + the identity of that revision. This will let you reproduce that + release at a later date, for whatever purpose you might need at + the time (reproducing a bug, porting to a new platform, etc). + &interaction.tag.init;</para> + + <para id="x_36d">Mercurial lets you give a permanent name to any revision + using the <command role="hg-cmd">hg tag</command> command. Not + surprisingly, these names are called <quote>tags</quote>.</para> + + &interaction.tag.tag; + + <para id="x_36e">A tag is nothing more than a <quote>symbolic name</quote> + for a revision. Tags exist purely for your convenience, so that + you have a handy permanent way to refer to a revision; Mercurial + doesn't interpret the tag names you use in any way. Neither + does Mercurial place any restrictions on the name of a tag, + beyond a few that are necessary to ensure that a tag can be + parsed unambiguously. A tag name cannot contain any of the + following characters:</para> + <itemizedlist> + <listitem><para id="x_36f">Colon (ASCII 58, + <quote><literal>:</literal></quote>)</para> + </listitem> + <listitem><para id="x_370">Carriage return (ASCII 13, + <quote><literal>\r</literal></quote>)</para> + </listitem> + <listitem><para id="x_371">Newline (ASCII 10, + <quote><literal>\n</literal></quote>)</para> + </listitem></itemizedlist> + + <para id="x_372">You can use the <command role="hg-cmd">hg tags</command> + command to display the tags present in your repository. In the + output, each tagged revision is identified first by its name, + then by revision number, and finally by the unique hash of the + revision.</para> + + &interaction.tag.tags; + + <para id="x_373">Notice that <literal>tip</literal> is listed in the output + of <command role="hg-cmd">hg tags</command>. The + <literal>tip</literal> tag is a special <quote>floating</quote> + tag, which always identifies the newest revision in the + repository.</para> + + <para id="x_374">In the output of the <command role="hg-cmd">hg + tags</command> command, tags are listed in reverse order, by + revision number. This usually means that recent tags are listed + before older tags. It also means that <literal>tip</literal> is + always going to be the first tag listed in the output of + <command role="hg-cmd">hg tags</command>.</para> + + <para id="x_375">When you run <command role="hg-cmd">hg log</command>, if it + displays a revision that has tags associated with it, it will + print those tags.</para> + + &interaction.tag.log; + + <para id="x_376">Any time you need to provide a revision ID to a Mercurial + command, the command will accept a tag name in its place. + Internally, Mercurial will translate your tag name into the + corresponding revision ID, then use that.</para> + + &interaction.tag.log.v1.0; + + <para id="x_377">There's no limit on the number of tags you can have in a + repository, or on the number of tags that a single revision can + have. As a practical matter, it's not a great idea to have + <quote>too many</quote> (a number which will vary from project + to project), simply because tags are supposed to help you to + find revisions. If you have lots of tags, the ease of using + them to identify revisions diminishes rapidly.</para> + + <para id="x_378">For example, if your project has milestones as frequent as + every few days, it's perfectly reasonable to tag each one of + those. But if you have a continuous build system that makes + sure every revision can be built cleanly, you'd be introducing a + lot of noise if you were to tag every clean build. Instead, you + could tag failed builds (on the assumption that they're rare!), + or simply not use tags to track buildability.</para> + + <para id="x_379">If you want to remove a tag that you no longer want, use + <command role="hg-cmd">hg tag --remove</command>.</para> + + &interaction.tag.remove; + + <para id="x_37a">You can also modify a tag at any time, so that it identifies + a different revision, by simply issuing a new <command + role="hg-cmd">hg tag</command> command. You'll have to use the + <option role="hg-opt-tag">-f</option> option to tell Mercurial + that you <emphasis>really</emphasis> want to update the + tag.</para> + + &interaction.tag.replace; + + <para id="x_37b">There will still be a permanent record of the previous + identity of the tag, but Mercurial will no longer use it. + There's thus no penalty to tagging the wrong revision; all you + have to do is turn around and tag the correct revision once you + discover your error.</para> + + <para id="x_37c">Mercurial stores tags in a normal revision-controlled file + in your repository. If you've created any tags, you'll find + them in a file in the root of your repository named <filename + role="special">.hgtags</filename>. When you run the <command + role="hg-cmd">hg tag</command> command, Mercurial modifies + this file, then automatically commits the change to it. This + means that every time you run <command role="hg-cmd">hg + tag</command>, you'll see a corresponding changeset in the + output of <command role="hg-cmd">hg log</command>.</para> + + &interaction.tag.tip; + + <sect2> + <title>Handling tag conflicts during a merge</title> + + <para id="x_37d">You won't often need to care about the <filename + role="special">.hgtags</filename> file, but it sometimes + makes its presence known during a merge. The format of the + file is simple: it consists of a series of lines. Each line + starts with a changeset hash, followed by a space, followed by + the name of a tag.</para> + + <para id="x_37e">If you're resolving a conflict in the <filename + role="special">.hgtags</filename> file during a merge, + there's one twist to modifying the <filename + role="special">.hgtags</filename> file: when Mercurial is + parsing the tags in a repository, it + <emphasis>never</emphasis> reads the working copy of the + <filename role="special">.hgtags</filename> file. Instead, it + reads the <emphasis>most recently committed</emphasis> + revision of the file.</para> + + <para id="x_37f">An unfortunate consequence of this design is that you + can't actually verify that your merged <filename + role="special">.hgtags</filename> file is correct until + <emphasis>after</emphasis> you've committed a change. So if + you find yourself resolving a conflict on <filename + role="special">.hgtags</filename> during a merge, be sure to + run <command role="hg-cmd">hg tags</command> after you commit. + If it finds an error in the <filename + role="special">.hgtags</filename> file, it will report the + location of the error, which you can then fix and commit. You + should then run <command role="hg-cmd">hg tags</command> + again, just to be sure that your fix is correct.</para> + </sect2> + + <sect2> + <title>Tags and cloning</title> + + <para id="x_380">You may have noticed that the <command role="hg-cmd">hg + clone</command> command has a <option + role="hg-opt-clone">-r</option> option that lets you clone + an exact copy of the repository as of a particular changeset. + The new clone will not contain any project history that comes + after the revision you specified. This has an interaction + with tags that can surprise the unwary.</para> + + <para id="x_381">Recall that a tag is stored as a revision to + the <filename role="special">.hgtags</filename> file. When you + create a tag, the changeset in which its recorded refers to an + older changeset. When you run <command role="hg-cmd">hg clone + -r foo</command> to clone a repository as of tag + <literal>foo</literal>, the new clone <emphasis>will not + contain any revision newer than the one the tag refers to, + including the revision where the tag was created</emphasis>. + The result is that you'll get exactly the right subset of the + project's history in the new repository, but + <emphasis>not</emphasis> the tag you might have + expected.</para> + </sect2> + + <sect2> + <title>When permanent tags are too much</title> + + <para id="x_382">Since Mercurial's tags are revision controlled and carried + around with a project's history, everyone you work with will + see the tags you create. But giving names to revisions has + uses beyond simply noting that revision + <literal>4237e45506ee</literal> is really + <literal>v2.0.2</literal>. If you're trying to track down a + subtle bug, you might want a tag to remind you of something + like <quote>Anne saw the symptoms with this + revision</quote>.</para> + + <para id="x_383">For cases like this, what you might want to use are + <emphasis>local</emphasis> tags. You can create a local tag + with the <option role="hg-opt-tag">-l</option> option to the + <command role="hg-cmd">hg tag</command> command. This will + store the tag in a file called <filename + role="special">.hg/localtags</filename>. Unlike <filename + role="special">.hgtags</filename>, <filename + role="special">.hg/localtags</filename> is not revision + controlled. Any tags you create using <option + role="hg-opt-tag">-l</option> remain strictly local to the + repository you're currently working in.</para> + </sect2> + </sect1> + + <sect1> + <title>The flow of changes&emdash;big picture vs. little</title> + + <para id="x_384">To return to the outline I sketched at the + beginning of the chapter, let's think about a project that has + multiple concurrent pieces of work under development at + once.</para> + + <para id="x_385">There might be a push for a new <quote>main</quote> release; + a new minor bugfix release to the last main release; and an + unexpected <quote>hot fix</quote> to an old release that is now + in maintenance mode.</para> + + <para id="x_386">The usual way people refer to these different concurrent + directions of development is as <quote>branches</quote>. + However, we've already seen numerous times that Mercurial treats + <emphasis>all of history</emphasis> as a series of branches and + merges. Really, what we have here is two ideas that are + peripherally related, but which happen to share a name.</para> + <itemizedlist> + <listitem><para id="x_387"><quote>Big picture</quote> branches represent + the sweep of a project's evolution; people give them names, + and talk about them in conversation.</para> + </listitem> + <listitem><para id="x_388"><quote>Little picture</quote> branches are + artefacts of the day-to-day activity of developing and + merging changes. They expose the narrative of how the code + was developed.</para> + </listitem></itemizedlist> + </sect1> + + <sect1> + <title>Managing big-picture branches in repositories</title> + + <para id="x_389">The easiest way to isolate a <quote>big picture</quote> + branch in Mercurial is in a dedicated repository. If you have + an existing shared repository&emdash;let's call it + <literal>myproject</literal>&emdash;that reaches a + <quote>1.0</quote> milestone, you can start to prepare for + future maintenance releases on top of version 1.0 by tagging the + revision from which you prepared the 1.0 release.</para> + + &interaction.branch-repo.tag; + + <para id="x_38a">You can then clone a new shared + <literal>myproject-1.0.1</literal> repository as of that + tag.</para> + + &interaction.branch-repo.clone; + + <para id="x_38b">Afterwards, if someone needs to work on a bug fix that ought + to go into an upcoming 1.0.1 minor release, they clone the + <literal>myproject-1.0.1</literal> repository, make their + changes, and push them back.</para> + + &interaction.branch-repo.bugfix; + + <para id="x_38c">Meanwhile, development for + the next major release can continue, isolated and unabated, in + the <literal>myproject</literal> repository.</para> + + &interaction.branch-repo.new; + </sect1> + + <sect1> + <title>Don't repeat yourself: merging across branches</title> + + <para id="x_38d">In many cases, if you have a bug to fix on a maintenance + branch, the chances are good that the bug exists on your + project's main branch (and possibly other maintenance branches, + too). It's a rare developer who wants to fix the same bug + multiple times, so let's look at a few ways that Mercurial can + help you to manage these bugfixes without duplicating your + work.</para> + + <para id="x_38e">In the simplest instance, all you need to do is pull changes + from your maintenance branch into your local clone of the target + branch.</para> + + &interaction.branch-repo.pull; + + <para id="x_38f">You'll then need to merge the heads of the two branches, and + push back to the main branch.</para> + + &interaction.branch-repo.merge; + </sect1> + + <sect1> + <title>Naming branches within one repository</title> + + <para id="x_390">In most instances, isolating branches in repositories is the + right approach. Its simplicity makes it easy to understand; and + so it's hard to make mistakes. There's a one-to-one + relationship between branches you're working in and directories + on your system. This lets you use normal (non-Mercurial-aware) + tools to work on files within a branch/repository.</para> + + <para id="x_391">If you're more in the <quote>power user</quote> category + (<emphasis>and</emphasis> your collaborators are too), there is + an alternative way of handling branches that you can consider. + I've already mentioned the human-level distinction between + <quote>small picture</quote> and <quote>big picture</quote> + branches. While Mercurial works with multiple <quote>small + picture</quote> branches in a repository all the time (for + example after you pull changes in, but before you merge them), + it can <emphasis>also</emphasis> work with multiple <quote>big + picture</quote> branches.</para> + + <para id="x_392">The key to working this way is that Mercurial lets you + assign a persistent <emphasis>name</emphasis> to a branch. + There always exists a branch named <literal>default</literal>. + Even before you start naming branches yourself, you can find + traces of the <literal>default</literal> branch if you look for + them.</para> + + <para id="x_393">As an example, when you run the <command role="hg-cmd">hg + commit</command> command, and it pops up your editor so that + you can enter a commit message, look for a line that contains + the text <quote><literal>HG: branch default</literal></quote> at + the bottom. This is telling you that your commit will occur on + the branch named <literal>default</literal>.</para> + + <para id="x_394">To start working with named branches, use the <command + role="hg-cmd">hg branches</command> command. This command + lists the named branches already present in your repository, + telling you which changeset is the tip of each.</para> + + &interaction.branch-named.branches; + + <para id="x_395">Since you haven't created any named branches yet, the only + one that exists is <literal>default</literal>.</para> + + <para id="x_396">To find out what the <quote>current</quote> branch is, run + the <command role="hg-cmd">hg branch</command> command, giving + it no arguments. This tells you what branch the parent of the + current changeset is on.</para> + + &interaction.branch-named.branch; + + <para id="x_397">To create a new branch, run the <command role="hg-cmd">hg + branch</command> command again. This time, give it one + argument: the name of the branch you want to create.</para> + + &interaction.branch-named.create; + + <para id="x_398">After you've created a branch, you might wonder what effect + the <command role="hg-cmd">hg branch</command> command has had. + What do the <command role="hg-cmd">hg status</command> and + <command role="hg-cmd">hg tip</command> commands report?</para> + + &interaction.branch-named.status; + + <para id="x_399">Nothing has changed in the + working directory, and there's been no new history created. As + this suggests, running the <command role="hg-cmd">hg + branch</command> command has no permanent effect; it only + tells Mercurial what branch name to use the + <emphasis>next</emphasis> time you commit a changeset.</para> + + <para id="x_39a">When you commit a change, Mercurial records the name of the + branch on which you committed. Once you've switched from the + <literal>default</literal> branch to another and committed, + you'll see the name of the new branch show up in the output of + <command role="hg-cmd">hg log</command>, <command + role="hg-cmd">hg tip</command>, and other commands that + display the same kind of output.</para> + + &interaction.branch-named.commit; + + <para id="x_39b">The <command role="hg-cmd">hg log</command>-like commands + will print the branch name of every changeset that's not on the + <literal>default</literal> branch. As a result, if you never + use named branches, you'll never see this information.</para> + + <para id="x_39c">Once you've named a branch and committed a change with that + name, every subsequent commit that descends from that change + will inherit the same branch name. You can change the name of a + branch at any time, using the <command role="hg-cmd">hg + branch</command> command.</para> + + &interaction.branch-named.rebranch; + + <para id="x_39d">In practice, this is something you won't do very often, as + branch names tend to have fairly long lifetimes. (This isn't a + rule, just an observation.)</para> + </sect1> + + <sect1> + <title>Dealing with multiple named branches in a + repository</title> + + <para id="x_39e">If you have more than one named branch in a repository, + Mercurial will remember the branch that your working directory + is on when you start a command like <command role="hg-cmd">hg + update</command> or <command role="hg-cmd">hg pull + -u</command>. It will update the working directory to the tip + of this branch, no matter what the <quote>repo-wide</quote> tip + is. To update to a revision that's on a different named branch, + you may need to use the <option role="hg-opt-update">-C</option> + option to <command role="hg-cmd">hg update</command>.</para> + + <para id="x_39f">This behavior is a little subtle, so let's see it in + action. First, let's remind ourselves what branch we're + currently on, and what branches are in our repository.</para> + + &interaction.branch-named.parents; + + <para id="x_3a0">We're on the <literal>bar</literal> branch, but there also + exists an older <command role="hg-cmd">hg foo</command> + branch.</para> + + <para id="x_3a1">We can <command role="hg-cmd">hg update</command> back and + forth between the tips of the <literal>foo</literal> and + <literal>bar</literal> branches without needing to use the + <option role="hg-opt-update">-C</option> option, because this + only involves going backwards and forwards linearly through our + change history.</para> + + &interaction.branch-named.update-switchy; + + <para id="x_3a2">If we go back to the <literal>foo</literal> branch and then + run <command role="hg-cmd">hg update</command>, it will keep us + on <literal>foo</literal>, not move us to the tip of + <literal>bar</literal>.</para> + + &interaction.branch-named.update-nothing; + + <para id="x_3a3">Committing a new change on the <literal>foo</literal> branch + introduces a new head.</para> + + &interaction.branch-named.foo-commit; + </sect1> + + <sect1> + <title>Branch names and merging</title> + + <para id="x_3a4">As you've probably noticed, merges in Mercurial are not + symmetrical. Let's say our repository has two heads, 17 and 23. + If I <command role="hg-cmd">hg update</command> to 17 and then + <command role="hg-cmd">hg merge</command> with 23, Mercurial + records 17 as the first parent of the merge, and 23 as the + second. Whereas if I <command role="hg-cmd">hg update</command> + to 23 and then <command role="hg-cmd">hg merge</command> with + 17, it records 23 as the first parent, and 17 as the + second.</para> + + <para id="x_3a5">This affects Mercurial's choice of branch name when you + merge. After a merge, Mercurial will retain the branch name of + the first parent when you commit the result of the merge. If + your first parent's branch name is <literal>foo</literal>, and + you merge with <literal>bar</literal>, the branch name will + still be <literal>foo</literal> after you merge.</para> + + <para id="x_3a6">It's not unusual for a repository to contain multiple heads, + each with the same branch name. Let's say I'm working on the + <literal>foo</literal> branch, and so are you. We commit + different changes; I pull your changes; I now have two heads, + each claiming to be on the <literal>foo</literal> branch. The + result of a merge will be a single head on the + <literal>foo</literal> branch, as you might hope.</para> + + <para id="x_3a7">But if I'm working on the <literal>bar</literal> branch, and + I merge work from the <literal>foo</literal> branch, the result + will remain on the <literal>bar</literal> branch.</para> + + &interaction.branch-named.merge; + + <para id="x_3a8">To give a more concrete example, if I'm working on the + <literal>bleeding-edge</literal> branch, and I want to bring in + the latest fixes from the <literal>stable</literal> branch, + Mercurial will choose the <quote>right</quote> + (<literal>bleeding-edge</literal>) branch name when I pull and + merge from <literal>stable</literal>.</para> + </sect1> + + <sect1> + <title>Branch naming is generally useful</title> + + <para id="x_3a9">You shouldn't think of named branches as applicable only to + situations where you have multiple long-lived branches + cohabiting in a single repository. They're very useful even in + the one-branch-per-repository case.</para> + + <para id="x_3aa">In the simplest case, giving a name to each branch gives you + a permanent record of which branch a changeset originated on. + This gives you more context when you're trying to follow the + history of a long-lived branchy project.</para> + + <para id="x_3ab">If you're working with shared repositories, you can set up a + <literal role="hook">pretxnchangegroup</literal> hook on each + that will block incoming changes that have the + <quote>wrong</quote> branch name. This provides a simple, but + effective, defence against people accidentally pushing changes + from a <quote>bleeding edge</quote> branch to a + <quote>stable</quote> branch. Such a hook might look like this + inside the shared repo's <filename role="special"> + /.hgrc</filename>.</para> + <programlisting>[hooks] +pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch</programlisting> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch09-undo.xml --- a/fr/ch09-undo.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch09-undo.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,963 +1,1201 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Finding and fixing your mistakes</title> -<para>\label{chap:undo}</para> - -<para>To err might be human, but to really handle the consequences well -takes a top-notch revision control system. In this chapter, we'll -discuss some of the techniques you can use when you find that a -problem has crept into your project. Mercurial has some highly -capable features that will help you to isolate the sources of -problems, and to handle them appropriately.</para> - -<sect1> -<title>Erasing local history</title> - -<sect2> -<title>The accidental commit</title> - -<para>I have the occasional but persistent problem of typing rather more -quickly than I can think, which sometimes results in me committing a -changeset that is either incomplete or plain wrong. In my case, the -usual kind of incomplete changeset is one in which I've created a new -source file, but forgotten to <command role="hg-cmd">hg add</command> it. A <quote>plain wrong</quote> -changeset is not as common, but no less annoying.</para> - -</sect2> -<sect2> -<title>Rolling back a transaction</title> -<para>\label{sec:undo:rollback}</para> - -<para>In section <xref linkend="sec:concepts:txn"/>, I mentioned that Mercurial treats -each modification of a repository as a <emphasis>transaction</emphasis>. Every time -you commit a changeset or pull changes from another repository, -Mercurial remembers what you did. You can undo, or <emphasis>roll back</emphasis>, -exactly one of these actions using the <command role="hg-cmd">hg rollback</command> command. (See -section <xref linkend="sec:undo:rollback-after-push"/> for an important caveat -about the use of this command.)</para> - -<para>Here's a mistake that I often find myself making: committing a change -in which I've created a new file, but forgotten to <command role="hg-cmd">hg add</command> it. -<!-- &interaction.rollback.commit; --> -Looking at the output of <command role="hg-cmd">hg status</command> after the commit immediately -confirms the error. -<!-- &interaction.rollback.status; --> -The commit captured the changes to the file <filename>a</filename>, but not the -new file <filename>b</filename>. If I were to push this changeset to a -repository that I shared with a colleague, the chances are high that -something in <filename>a</filename> would refer to <filename>b</filename>, which would not -be present in their repository when they pulled my changes. I would -thus become the object of some indignation.</para> - -<para>However, luck is with me&emdash;I've caught my error before I pushed the -changeset. I use the <command role="hg-cmd">hg rollback</command> command, and Mercurial makes -that last changeset vanish. -<!-- &interaction.rollback.rollback; --> -Notice that the changeset is no longer present in the repository's -history, and the working directory once again thinks that the file -<filename>a</filename> is modified. The commit and rollback have left the -working directory exactly as it was prior to the commit; the changeset -has been completely erased. I can now safely <command role="hg-cmd">hg add</command> the file -<filename>b</filename>, and rerun my commit. -<!-- &interaction.rollback.add; --></para> - -</sect2> -<sect2> -<title>The erroneous pull</title> - -<para>It's common practice with Mercurial to maintain separate development -branches of a project in different repositories. Your development -team might have one shared repository for your project's <quote>0.9</quote> -release, and another, containing different changes, for the <quote>1.0</quote> -release.</para> - -<para>Given this, you can imagine that the consequences could be messy if -you had a local <quote>0.9</quote> repository, and accidentally pulled changes -from the shared <quote>1.0</quote> repository into it. At worst, you could be -paying insufficient attention, and push those changes into the shared -<quote>0.9</quote> tree, confusing your entire team (but don't worry, we'll -return to this horror scenario later). However, it's more likely that -you'll notice immediately, because Mercurial will display the URL it's -pulling from, or you will see it pull a suspiciously large number of -changes into the repository. -</para> - -<para>The <command role="hg-cmd">hg rollback</command> command will work nicely to expunge all of the -changesets that you just pulled. Mercurial groups all changes from -one <command role="hg-cmd">hg pull</command> into a single transaction, so one <command role="hg-cmd">hg rollback</command> is -all you need to undo this mistake. -</para> - -</sect2> -<sect2> -<title>Rolling back is useless once you've pushed</title> -<para>\label{sec:undo:rollback-after-push} -</para> - -<para>The value of the <command role="hg-cmd">hg rollback</command> command drops to zero once you've -pushed your changes to another repository. Rolling back a change -makes it disappear entirely, but <emphasis>only</emphasis> in the repository in -which you perform the <command role="hg-cmd">hg rollback</command>. Because a rollback eliminates -history, there's no way for the disappearance of a change to propagate -between repositories. -</para> - -<para>If you've pushed a change to another repository&emdash;particularly if it's -a shared repository&emdash;it has essentially <quote>escaped into the wild,</quote> -and you'll have to recover from your mistake in a different way. What -will happen if you push a changeset somewhere, then roll it back, then -pull from the repository you pushed to, is that the changeset will -reappear in your repository. -</para> - -<para>(If you absolutely know for sure that the change you want to roll back -is the most recent change in the repository that you pushed to, -<emphasis>and</emphasis> you know that nobody else could have pulled it from that -repository, you can roll back the changeset there, too, but you really -should really not rely on this working reliably. If you do this, -sooner or later a change really will make it into a repository that -you don't directly control (or have forgotten about), and come back to -bite you.) -</para> - -</sect2> -<sect2> -<title>You can only roll back once</title> - -<para>Mercurial stores exactly one transaction in its transaction log; that -transaction is the most recent one that occurred in the repository. -This means that you can only roll back one transaction. If you expect -to be able to roll back one transaction, then its predecessor, this is -not the behaviour you will get. -<!-- &interaction.rollback.twice; --> -Once you've rolled back one transaction in a repository, you can't -roll back again in that repository until you perform another commit or -pull. -</para> - -</sect2> -</sect1> -<sect1> -<title>Reverting the mistaken change</title> - -<para>If you make a modification to a file, and decide that you really -didn't want to change the file at all, and you haven't yet committed -your changes, the <command role="hg-cmd">hg revert</command> command is the one you'll need. It -looks at the changeset that's the parent of the working directory, and -restores the contents of the file to their state as of that changeset. -(That's a long-winded way of saying that, in the normal case, it -undoes your modifications.) -</para> - -<para>Let's illustrate how the <command role="hg-cmd">hg revert</command> command works with yet another -small example. We'll begin by modifying a file that Mercurial is -already tracking. -<!-- &interaction.daily.revert.modify; --> -If we don't want that change, we can simply <command role="hg-cmd">hg revert</command> the file. -<!-- &interaction.daily.revert.unmodify; --> -The <command role="hg-cmd">hg revert</command> command provides us with an extra degree of safety -by saving our modified file with a <filename>.orig</filename> extension. -<!-- &interaction.daily.revert.status; --> -</para> - -<para>Here is a summary of the cases that the <command role="hg-cmd">hg revert</command> command can -deal with. We will describe each of these in more detail in the -section that follows. -</para> -<itemizedlist> -<listitem><para>If you modify a file, it will restore the file to its unmodified - state. -</para> -</listitem> -<listitem><para>If you <command role="hg-cmd">hg add</command> a file, it will undo the <quote>added</quote> state of - the file, but leave the file itself untouched. -</para> -</listitem> -<listitem><para>If you delete a file without telling Mercurial, it will restore - the file to its unmodified contents. -</para> -</listitem> -<listitem><para>If you use the <command role="hg-cmd">hg remove</command> command to remove a file, it will - undo the <quote>removed</quote> state of the file, and restore the file to its - unmodified contents. -</para> -</listitem></itemizedlist> - -<sect2> -<title>File management errors</title> -<para>\label{sec:undo:mgmt} -</para> - -<para>The <command role="hg-cmd">hg revert</command> command is useful for more than just modified -files. It lets you reverse the results of all of Mercurial's file -management commands&emdash;<command role="hg-cmd">hg add</command>, <command role="hg-cmd">hg remove</command>, and so on. -</para> - -<para>If you <command role="hg-cmd">hg add</command> a file, then decide that in fact you don't want -Mercurial to track it, use <command role="hg-cmd">hg revert</command> to undo the add. Don't -worry; Mercurial will not modify the file in any way. It will just -<quote>unmark</quote> the file. -<!-- &interaction.daily.revert.add; --> -</para> - -<para>Similarly, if you ask Mercurial to <command role="hg-cmd">hg remove</command> a file, you can use -<command role="hg-cmd">hg revert</command> to restore it to the contents it had as of the parent -of the working directory. -<!-- &interaction.daily.revert.remove; --> -This works just as well for a file that you deleted by hand, without -telling Mercurial (recall that in Mercurial terminology, this kind of -file is called <quote>missing</quote>). -<!-- &interaction.daily.revert.missing; --> -</para> - -<para>If you revert a <command role="hg-cmd">hg copy</command>, the copied-to file remains in your -working directory afterwards, untracked. Since a copy doesn't affect -the copied-from file in any way, Mercurial doesn't do anything with -the copied-from file. -<!-- &interaction.daily.revert.copy; --> -</para> - -<sect3> -<title>A slightly special case: reverting a rename</title> - -<para>If you <command role="hg-cmd">hg rename</command> a file, there is one small detail that -you should remember. When you <command role="hg-cmd">hg revert</command> a rename, it's not -enough to provide the name of the renamed-to file, as you can see -here. -<!-- &interaction.daily.revert.rename; --> -As you can see from the output of <command role="hg-cmd">hg status</command>, the renamed-to file -is no longer identified as added, but the renamed-<emphasis>from</emphasis> file is -still removed! This is counter-intuitive (at least to me), but at -least it's easy to deal with. -<!-- &interaction.daily.revert.rename-orig; --> -So remember, to revert a <command role="hg-cmd">hg rename</command>, you must provide <emphasis>both</emphasis> -the source and destination names. -</para> - -<para>% TODO: the output doesn't look like it will be removed! -</para> - -<para>(By the way, if you rename a file, then modify the renamed-to file, -then revert both components of the rename, when Mercurial restores the -file that was removed as part of the rename, it will be unmodified. -If you need the modifications in the renamed-to file to show up in the -renamed-from file, don't forget to copy them over.) -</para> - -<para>These fiddly aspects of reverting a rename arguably constitute a small -bug in Mercurial. -</para> - -</sect3> -</sect2> -</sect1> -<sect1> -<title>Dealing with committed changes</title> - -<para>Consider a case where you have committed a change $a$, and another -change $b$ on top of it; you then realise that change $a$ was -incorrect. Mercurial lets you <quote>back out</quote> an entire changeset -automatically, and building blocks that let you reverse part of a -changeset by hand. -</para> - -<para>Before you read this section, here's something to keep in mind: the -<command role="hg-cmd">hg backout</command> command undoes changes by <emphasis>adding</emphasis> history, not -by modifying or erasing it. It's the right tool to use if you're -fixing bugs, but not if you're trying to undo some change that has -catastrophic consequences. To deal with those, see -section <xref linkend="sec:undo:aaaiiieee"/>. -</para> - -<sect2> -<title>Backing out a changeset</title> - -<para>The <command role="hg-cmd">hg backout</command> command lets you <quote>undo</quote> the effects of an entire -changeset in an automated fashion. Because Mercurial's history is -immutable, this command <emphasis>does not</emphasis> get rid of the changeset you -want to undo. Instead, it creates a new changeset that -<emphasis>reverses</emphasis> the effect of the to-be-undone changeset. -</para> - -<para>The operation of the <command role="hg-cmd">hg backout</command> command is a little intricate, so -let's illustrate it with some examples. First, we'll create a -repository with some simple changes. -<!-- &interaction.backout.init; --> -</para> - -<para>The <command role="hg-cmd">hg backout</command> command takes a single changeset ID as its -argument; this is the changeset to back out. Normally, -<command role="hg-cmd">hg backout</command> will drop you into a text editor to write a commit -message, so you can record why you're backing the change out. In this -example, we provide a commit message on the command line using the -<option role="hg-opt-backout">-m</option> option. -</para> - -</sect2> -<sect2> -<title>Backing out the tip changeset</title> - -<para>We're going to start by backing out the last changeset we committed. -<!-- &interaction.backout.simple; --> -You can see that the second line from <filename>myfile</filename> is no longer -present. Taking a look at the output of <command role="hg-cmd">hg log</command> gives us an idea -of what the <command role="hg-cmd">hg backout</command> command has done. -<!-- &interaction.backout.simple.log; --> -Notice that the new changeset that <command role="hg-cmd">hg backout</command> has created is a -child of the changeset we backed out. It's easier to see this in -figure <xref linkend="fig:undo:backout"/>, which presents a graphical view of the -change history. As you can see, the history is nice and linear. -</para> - -<informalfigure> - -<para> <mediaobject><imageobject><imagedata fileref="undo-simple"/></imageobject><textobject><phrase>XXX add text</phrase></textobject></mediaobject> - <caption><para>Backing out a change using the <command role="hg-cmd">hg backout</command> command</para></caption> - \label{fig:undo:backout} -</para> -</informalfigure> - -</sect2> -<sect2> -<title>Backing out a non-tip change</title> - -<para>If you want to back out a change other than the last one you -committed, pass the <option role="hg-opt-backout">--merge</option> option to the -<command role="hg-cmd">hg backout</command> command. -<!-- &interaction.backout.non-tip.clone; --> -This makes backing out any changeset a <quote>one-shot</quote> operation that's -usually simple and fast. -<!-- &interaction.backout.non-tip.backout; --> -</para> - -<para>If you take a look at the contents of <filename>myfile</filename> after the -backout finishes, you'll see that the first and third changes are -present, but not the second. -<!-- &interaction.backout.non-tip.cat; --> -</para> - -<para>As the graphical history in figure <xref linkend="fig:undo:backout-non-tip"/> -illustrates, Mercurial actually commits <emphasis>two</emphasis> changes in this -kind of situation (the box-shaped nodes are the ones that Mercurial -commits automatically). Before Mercurial begins the backout process, -it first remembers what the current parent of the working directory -is. It then backs out the target changeset, and commits that as a -changeset. Finally, it merges back to the previous parent of the -working directory, and commits the result of the merge. -</para> - -<para>% TODO: to me it looks like mercurial doesn't commit the second merge automatically! -</para> - -<informalfigure> - -<para> <mediaobject><imageobject><imagedata fileref="undo-non-tip"/></imageobject><textobject><phrase>XXX add text</phrase></textobject></mediaobject> - <caption><para>Automated backout of a non-tip change using the <command role="hg-cmd">hg backout</command> command</para></caption> - \label{fig:undo:backout-non-tip} -</para> -</informalfigure> - -<para>The result is that you end up <quote>back where you were</quote>, only with some -extra history that undoes the effect of the changeset you wanted to -back out. -</para> - -<sect3> -<title>Always use the <option role="hg-opt-backout">--merge</option> option</title> - -<para>In fact, since the <option role="hg-opt-backout">--merge</option> option will do the <quote>right -thing</quote> whether or not the changeset you're backing out is the tip -(i.e. it won't try to merge if it's backing out the tip, since there's -no need), you should <emphasis>always</emphasis> use this option when you run the -<command role="hg-cmd">hg backout</command> command. -</para> - -</sect3> -</sect2> -<sect2> -<title>Gaining more control of the backout process</title> - -<para>While I've recommended that you always use the -<option role="hg-opt-backout">--merge</option> option when backing out a change, the -<command role="hg-cmd">hg backout</command> command lets you decide how to merge a backout -changeset. Taking control of the backout process by hand is something -you will rarely need to do, but it can be useful to understand what -the <command role="hg-cmd">hg backout</command> command is doing for you automatically. To -illustrate this, let's clone our first repository, but omit the -backout change that it contains. -</para> - -<para><!-- &interaction.backout.manual.clone; --> -As with our earlier example, We'll commit a third changeset, then back -out its parent, and see what happens. -<!-- &interaction.backout.manual.backout; --> -Our new changeset is again a descendant of the changeset we backout -out; it's thus a new head, <emphasis>not</emphasis> a descendant of the changeset -that was the tip. The <command role="hg-cmd">hg backout</command> command was quite explicit in -telling us this. -<!-- &interaction.backout.manual.log; --> -</para> - -<para>Again, it's easier to see what has happened by looking at a graph of -the revision history, in figure <xref linkend="fig:undo:backout-manual"/>. This -makes it clear that when we use <command role="hg-cmd">hg backout</command> to back out a change -other than the tip, Mercurial adds a new head to the repository (the -change it committed is box-shaped). -</para> - -<informalfigure> - -<para> <mediaobject><imageobject><imagedata fileref="undo-manual"/></imageobject><textobject><phrase>XXX add text</phrase></textobject></mediaobject> - <caption><para>Backing out a change using the <command role="hg-cmd">hg backout</command> command</para></caption> - \label{fig:undo:backout-manual} -</para> -</informalfigure> - -<para>After the <command role="hg-cmd">hg backout</command> command has completed, it leaves the new -<quote>backout</quote> changeset as the parent of the working directory. -<!-- &interaction.backout.manual.parents; --> -Now we have two isolated sets of changes. -<!-- &interaction.backout.manual.heads; --> -</para> - -<para>Let's think about what we expect to see as the contents of -<filename>myfile</filename> now. The first change should be present, because -we've never backed it out. The second change should be missing, as -that's the change we backed out. Since the history graph shows the -third change as a separate head, we <emphasis>don't</emphasis> expect to see the -third change present in <filename>myfile</filename>. -<!-- &interaction.backout.manual.cat; --> -To get the third change back into the file, we just do a normal merge -of our two heads. -<!-- &interaction.backout.manual.merge; --> -Afterwards, the graphical history of our repository looks like -figure <xref linkend="fig:undo:backout-manual-merge"/>. -</para> - -<informalfigure> - -<para> <mediaobject><imageobject><imagedata fileref="undo-manual-merge"/></imageobject><textobject><phrase>XXX add text</phrase></textobject></mediaobject> - <caption><para>Manually merging a backout change</para></caption> - \label{fig:undo:backout-manual-merge} -</para> -</informalfigure> - -</sect2> -<sect2> -<title>Why <command role="hg-cmd">hg backout</command> works as it does</title> - -<para>Here's a brief description of how the <command role="hg-cmd">hg backout</command> command works. -</para> -<orderedlist> -<listitem><para>It ensures that the working directory is <quote>clean</quote>, i.e. that - the output of <command role="hg-cmd">hg status</command> would be empty. -</para> -</listitem> -<listitem><para>It remembers the current parent of the working directory. Let's - call this changeset <literal>orig</literal> -</para> -</listitem> -<listitem><para>It does the equivalent of a <command role="hg-cmd">hg update</command> to sync the working - directory to the changeset you want to back out. Let's call this - changeset <literal>backout</literal> -</para> -</listitem> -<listitem><para>It finds the parent of that changeset. Let's call that - changeset <literal>parent</literal>. -</para> -</listitem> -<listitem><para>For each file that the <literal>backout</literal> changeset affected, it - does the equivalent of a <command role="hg-cmd">hg revert -r parent</command> on that file, - to restore it to the contents it had before that changeset was - committed. -</para> -</listitem> -<listitem><para>It commits the result as a new changeset. This changeset has - <literal>backout</literal> as its parent. -</para> -</listitem> -<listitem><para>If you specify <option role="hg-opt-backout">--merge</option> on the command line, it - merges with <literal>orig</literal>, and commits the result of the merge. -</para> -</listitem></orderedlist> - -<para>An alternative way to implement the <command role="hg-cmd">hg backout</command> command would be -to <command role="hg-cmd">hg export</command> the to-be-backed-out changeset as a diff, then use -the <option role="cmd-opt-patch">--reverse</option> option to the <command>patch</command> command to -reverse the effect of the change without fiddling with the working -directory. This sounds much simpler, but it would not work nearly as -well. -</para> - -<para>The reason that <command role="hg-cmd">hg backout</command> does an update, a commit, a merge, and -another commit is to give the merge machinery the best chance to do a -good job when dealing with all the changes <emphasis>between</emphasis> the change -you're backing out and the current tip. -</para> - -<para>If you're backing out a changeset that's 100 revisions back in your -project's history, the chances that the <command>patch</command> command will -be able to apply a reverse diff cleanly are not good, because -intervening changes are likely to have <quote>broken the context</quote> that -<command>patch</command> uses to determine whether it can apply a patch (if -this sounds like gibberish, see <xref linkend="sec:mq:patch"/> for a -discussion of the <command>patch</command> command). Also, Mercurial's merge -machinery will handle files and directories being renamed, permission -changes, and modifications to binary files, none of which -<command>patch</command> can deal with. -</para> - -</sect2> -</sect1> -<sect1> -<title>Changes that should never have been</title> -<para>\label{sec:undo:aaaiiieee} -</para> - -<para>Most of the time, the <command role="hg-cmd">hg backout</command> command is exactly what you need -if you want to undo the effects of a change. It leaves a permanent -record of exactly what you did, both when committing the original -changeset and when you cleaned up after it. -</para> - -<para>On rare occasions, though, you may find that you've committed a change -that really should not be present in the repository at all. For -example, it would be very unusual, and usually considered a mistake, -to commit a software project's object files as well as its source -files. Object files have almost no intrinsic value, and they're -<emphasis>big</emphasis>, so they increase the size of the repository and the amount -of time it takes to clone or pull changes. -</para> - -<para>Before I discuss the options that you have if you commit a <quote>brown -paper bag</quote> change (the kind that's so bad that you want to pull a -brown paper bag over your head), let me first discuss some approaches -that probably won't work. -</para> - -<para>Since Mercurial treats history as accumulative&emdash;every change builds -on top of all changes that preceded it&emdash;you generally can't just make -disastrous changes disappear. The one exception is when you've just -committed a change, and it hasn't been pushed or pulled into another -repository. That's when you can safely use the <command role="hg-cmd">hg rollback</command> -command, as I detailed in section <xref linkend="sec:undo:rollback"/>. -</para> - -<para>After you've pushed a bad change to another repository, you -<emphasis>could</emphasis> still use <command role="hg-cmd">hg rollback</command> to make your local copy of the -change disappear, but it won't have the consequences you want. The -change will still be present in the remote repository, so it will -reappear in your local repository the next time you pull. -</para> - -<para>If a situation like this arises, and you know which repositories your -bad change has propagated into, you can <emphasis>try</emphasis> to get rid of the -changeefrom <emphasis>every</emphasis> one of those repositories. This is, of -course, not a satisfactory solution: if you miss even a single -repository while you're expunging, the change is still <quote>in the -wild</quote>, and could propagate further. -</para> - -<para>If you've committed one or more changes <emphasis>after</emphasis> the change that -you'd like to see disappear, your options are further reduced. -Mercurial doesn't provide a way to <quote>punch a hole</quote> in history, -leaving changesets intact. -</para> - -<para>XXX This needs filling out. The <literal>hg-replay</literal> script in the -<literal>examples</literal> directory works, but doesn't handle merge -changesets. Kind of an important omission. -</para> - -<sect2> -<title>Protect yourself from <quote>escaped</quote> changes</title> - -<para>If you've committed some changes to your local repository and they've -been pushed or pulled somewhere else, this isn't necessarily a -disaster. You can protect yourself ahead of time against some classes -of bad changeset. This is particularly easy if your team usually -pulls changes from a central repository. -</para> - -<para>By configuring some hooks on that repository to validate incoming -changesets (see chapter <xref linkend="chap:hook"/>), you can automatically -prevent some kinds of bad changeset from being pushed to the central -repository at all. With such a configuration in place, some kinds of -bad changeset will naturally tend to <quote>die out</quote> because they can't -propagate into the central repository. Better yet, this happens -without any need for explicit intervention. -</para> - -<para>For instance, an incoming change hook that verifies that a changeset -will actually compile can prevent people from inadvertantly <quote>breaking -the build</quote>. -</para> - -</sect2> -</sect1> -<sect1> -<title>Finding the source of a bug</title> -<para>\label{sec:undo:bisect} -</para> - -<para>While it's all very well to be able to back out a changeset that -introduced a bug, this requires that you know which changeset to back -out. Mercurial provides an invaluable command, called -<command role="hg-cmd">hg bisect</command>, that helps you to automate this process and accomplish -it very efficiently. -</para> - -<para>The idea behind the <command role="hg-cmd">hg bisect</command> command is that a changeset has -introduced some change of behaviour that you can identify with a -simple binary test. You don't know which piece of code introduced the -change, but you know how to test for the presence of the bug. The -<command role="hg-cmd">hg bisect</command> command uses your test to direct its search for the -changeset that introduced the code that caused the bug. -</para> - -<para>Here are a few scenarios to help you understand how you might apply -this command. -</para> -<itemizedlist> -<listitem><para>The most recent version of your software has a bug that you - remember wasn't present a few weeks ago, but you don't know when it - was introduced. Here, your binary test checks for the presence of - that bug. -</para> -</listitem> -<listitem><para>You fixed a bug in a rush, and now it's time to close the entry - in your team's bug database. The bug database requires a changeset - ID when you close an entry, but you don't remember which changeset - you fixed the bug in. Once again, your binary test checks for the - presence of the bug. -</para> -</listitem> -<listitem><para>Your software works correctly, but runs 15% slower than the - last time you measured it. You want to know which changeset - introduced the performance regression. In this case, your binary - test measures the performance of your software, to see whether it's - <quote>fast</quote> or <quote>slow</quote>. -</para> -</listitem> -<listitem><para>The sizes of the components of your project that you ship - exploded recently, and you suspect that something changed in the way - you build your project. -</para> -</listitem></itemizedlist> - -<para>From these examples, it should be clear that the <command role="hg-cmd">hg bisect</command> -command is not useful only for finding the sources of bugs. You can -use it to find any <quote>emergent property</quote> of a repository (anything -that you can't find from a simple text search of the files in the -tree) for which you can write a binary test. -</para> - -<para>We'll introduce a little bit of terminology here, just to make it -clear which parts of the search process are your responsibility, and -which are Mercurial's. A <emphasis>test</emphasis> is something that <emphasis>you</emphasis> run -when <command role="hg-cmd">hg bisect</command> chooses a changeset. A <emphasis>probe</emphasis> is what -<command role="hg-cmd">hg bisect</command> runs to tell whether a revision is good. Finally, -we'll use the word <quote>bisect</quote>, as both a noun and a verb, to stand in -for the phrase <quote>search using the <command role="hg-cmd">hg bisect</command> command. -</para> - -<para>One simple way to automate the searching process would be simply to -probe every changeset. However, this scales poorly. If it took ten -minutes to test a single changeset, and you had 10,000 changesets in -your repository, the exhaustive approach would take on average 35 -<emphasis>days</emphasis> to find the changeset that introduced a bug. Even if you -knew that the bug was introduced by one of the last 500 changesets, -and limited your search to those, you'd still be looking at over 40 -hours to find the changeset that introduced your bug. -</para> - -<para>What the <command role="hg-cmd">hg bisect</command> command does is use its knowledge of the -<quote>shape</quote> of your project's revision history to perform a search in -time proportional to the <emphasis>logarithm</emphasis> of the number of changesets -to check (the kind of search it performs is called a dichotomic -search). With this approach, searching through 10,000 changesets will -take less than three hours, even at ten minutes per test (the search -will require about 14 tests). Limit your search to the last hundred -changesets, and it will take only about an hour (roughly seven tests). -</para> - -<para>The <command role="hg-cmd">hg bisect</command> command is aware of the <quote>branchy</quote> nature of a -Mercurial project's revision history, so it has no problems dealing -with branches, merges, or multiple heads in a repository. It can -prune entire branches of history with a single probe, which is how it -operates so efficiently. -</para> - -<sect2> -<title>Using the <command role="hg-cmd">hg bisect</command> command</title> - -<para>Here's an example of <command role="hg-cmd">hg bisect</command> in action. -</para> - -<note> -<para> In versions 0.9.5 and earlier of Mercurial, <command role="hg-cmd">hg bisect</command> was not a - core command: it was distributed with Mercurial as an extension. - This section describes the built-in command, not the old extension. -</para> -</note> - -<para>Now let's create a repository, so that we can try out the -<command role="hg-cmd">hg bisect</command> command in isolation. -<!-- &interaction.bisect.init; --> -We'll simulate a project that has a bug in it in a simple-minded way: -create trivial changes in a loop, and nominate one specific change -that will have the <quote>bug</quote>. This loop creates 35 changesets, each -adding a single file to the repository. We'll represent our <quote>bug</quote> -with a file that contains the text <quote>i have a gub</quote>. -<!-- &interaction.bisect.commits; --> -</para> - -<para>The next thing that we'd like to do is figure out how to use the -<command role="hg-cmd">hg bisect</command> command. We can use Mercurial's normal built-in help -mechanism for this. -<!-- &interaction.bisect.help; --> -</para> - -<para>The <command role="hg-cmd">hg bisect</command> command works in steps. Each step proceeds as follows. -</para> -<orderedlist> -<listitem><para>You run your binary test. -</para> -</listitem><itemizedlist> -<listitem><para> \item If the test succeeded, you tell <command role="hg-cmd">hg bisect</command> by running the - <command role="hg-cmd">hg bisect good</command> command. - \item If it failed, run the <command role="hg-cmd">hg bisect --bad</command> command. -</para> -</listitem></itemizedlist> -<listitem><para>The command uses your information to decide which changeset to - test next. -</para> -</listitem> -<listitem><para>It updates the working directory to that changeset, and the - process begins again. -</para> -</listitem></orderedlist> -<para>The process ends when <command role="hg-cmd">hg bisect</command> identifies a unique changeset -that marks the point where your test transitioned from <quote>succeeding</quote> -to <quote>failing</quote>. -</para> - -<para>To start the search, we must run the <command role="hg-cmd">hg bisect --reset</command> command. -<!-- &interaction.bisect.search.init; --> -</para> - -<para>In our case, the binary test we use is simple: we check to see if any -file in the repository contains the string <quote>i have a gub</quote>. If it -does, this changeset contains the change that <quote>caused the bug</quote>. By -convention, a changeset that has the property we're searching for is -<quote>bad</quote>, while one that doesn't is <quote>good</quote>. -</para> - -<para>Most of the time, the revision to which the working directory is -synced (usually the tip) already exhibits the problem introduced by -the buggy change, so we'll mark it as <quote>bad</quote>. -<!-- &interaction.bisect.search.bad-init; --> -</para> - -<para>Our next task is to nominate a changeset that we know <emphasis>doesn't</emphasis> -have the bug; the <command role="hg-cmd">hg bisect</command> command will <quote>bracket</quote> its search -between the first pair of good and bad changesets. In our case, we -know that revision 10 didn't have the bug. (I'll have more words -about choosing the first <quote>good</quote> changeset later.) -<!-- &interaction.bisect.search.good-init; --> -</para> - -<para>Notice that this command printed some output. -</para> -<itemizedlist> -<listitem><para>It told us how many changesets it must consider before it can - identify the one that introduced the bug, and how many tests that - will require. -</para> -</listitem> -<listitem><para>It updated the working directory to the next changeset to test, - and told us which changeset it's testing. -</para> -</listitem></itemizedlist> - -<para>We now run our test in the working directory. We use the -<command>grep</command> command to see if our <quote>bad</quote> file is present in the -working directory. If it is, this revision is bad; if not, this -revision is good. -<!-- &interaction.bisect.search.step1; --> -</para> - -<para>This test looks like a perfect candidate for automation, so let's turn -it into a shell function. -<!-- &interaction.bisect.search.mytest; --> -We can now run an entire test step with a single command, -<literal>mytest</literal>. -<!-- &interaction.bisect.search.step2; --> -A few more invocations of our canned test step command, and we're -done. -<!-- &interaction.bisect.search.rest; --> -</para> - -<para>Even though we had 40 changesets to search through, the <command role="hg-cmd">hg bisect</command> -command let us find the changeset that introduced our <quote>bug</quote> with -only five tests. Because the number of tests that the <command role="hg-cmd">hg bisect</command> -command performs grows logarithmically with the number of changesets to -search, the advantage that it has over the <quote>brute force</quote> search -approach increases with every changeset you add. -</para> - -</sect2> -<sect2> -<title>Cleaning up after your search</title> - -<para>When you're finished using the <command role="hg-cmd">hg bisect</command> command in a -repository, you can use the <command role="hg-cmd">hg bisect reset</command> command to drop -the information it was using to drive your search. The command -doesn't use much space, so it doesn't matter if you forget to run this -command. However, <command role="hg-cmd">hg bisect</command> won't let you start a new search in -that repository until you do a <command role="hg-cmd">hg bisect reset</command>. -<!-- &interaction.bisect.search.reset; --> -</para> - -</sect2> -</sect1> -<sect1> -<title>Tips for finding bugs effectively</title> - -<sect2> -<title>Give consistent input</title> - -<para>The <command role="hg-cmd">hg bisect</command> command requires that you correctly report the -result of every test you perform. If you tell it that a test failed -when it really succeeded, it <emphasis>might</emphasis> be able to detect the -inconsistency. If it can identify an inconsistency in your reports, -it will tell you that a particular changeset is both good and bad. -However, it can't do this perfectly; it's about as likely to report -the wrong changeset as the source of the bug. -</para> - -</sect2> -<sect2> -<title>Automate as much as possible</title> - -<para>When I started using the <command role="hg-cmd">hg bisect</command> command, I tried a few times -to run my tests by hand, on the command line. This is an approach -that I, at least, am not suited to. After a few tries, I found that I -was making enough mistakes that I was having to restart my searches -several times before finally getting correct results. -</para> - -<para>My initial problems with driving the <command role="hg-cmd">hg bisect</command> command by hand -occurred even with simple searches on small repositories; if the -problem you're looking for is more subtle, or the number of tests that -<command role="hg-cmd">hg bisect</command> must perform increases, the likelihood of operator -error ruining the search is much higher. Once I started automating my -tests, I had much better results. -</para> - -<para>The key to automated testing is twofold: -</para> -<itemizedlist> -<listitem><para>always test for the same symptom, and -</para> -</listitem> -<listitem><para>always feed consistent input to the <command role="hg-cmd">hg bisect</command> command. -</para> -</listitem></itemizedlist> -<para>In my tutorial example above, the <command>grep</command> command tests for the -symptom, and the <literal>if</literal> statement takes the result of this check -and ensures that we always feed the same input to the <command role="hg-cmd">hg bisect</command> -command. The <literal>mytest</literal> function marries these together in a -reproducible way, so that every test is uniform and consistent. -</para> - -</sect2> -<sect2> -<title>Check your results</title> - -<para>Because the output of a <command role="hg-cmd">hg bisect</command> search is only as good as the -input you give it, don't take the changeset it reports as the -absolute truth. A simple way to cross-check its report is to manually -run your test at each of the following changesets: -</para> -<itemizedlist> -<listitem><para>The changeset that it reports as the first bad revision. Your - test should still report this as bad. -</para> -</listitem> -<listitem><para>The parent of that changeset (either parent, if it's a merge). - Your test should report this changeset as good. -</para> -</listitem> -<listitem><para>A child of that changeset. Your test should report this - changeset as bad. -</para> -</listitem></itemizedlist> - -</sect2> -<sect2> -<title>Beware interference between bugs</title> - -<para>It's possible that your search for one bug could be disrupted by the -presence of another. For example, let's say your software crashes at -revision 100, and worked correctly at revision 50. Unknown to you, -someone else introduced a different crashing bug at revision 60, and -fixed it at revision 80. This could distort your results in one of -several ways. -</para> - -<para>It is possible that this other bug completely <quote>masks</quote> yours, which -is to say that it occurs before your bug has a chance to manifest -itself. If you can't avoid that other bug (for example, it prevents -your project from building), and so can't tell whether your bug is -present in a particular changeset, the <command role="hg-cmd">hg bisect</command> command cannot -help you directly. Instead, you can mark a changeset as untested by -running <command role="hg-cmd">hg bisect --skip</command>. -</para> - -<para>A different problem could arise if your test for a bug's presence is -not specific enough. If you check for <quote>my program crashes</quote>, then -both your crashing bug and an unrelated crashing bug that masks it -will look like the same thing, and mislead <command role="hg-cmd">hg bisect</command>. -</para> - -<para>Another useful situation in which to use <command role="hg-cmd">hg bisect --skip</command> is -if you can't test a revision because your project was in a broken and -hence untestable state at that revision, perhaps because someone -checked in a change that prevented the project from building. -</para> - -</sect2> -<sect2> -<title>Bracket your search lazily</title> - -<para>Choosing the first <quote>good</quote> and <quote>bad</quote> changesets that will mark the -end points of your search is often easy, but it bears a little -discussion nevertheless. From the perspective of <command role="hg-cmd">hg bisect</command>, the -<quote>newest</quote> changeset is conventionally <quote>bad</quote>, and the older -changeset is <quote>good</quote>. -</para> - -<para>If you're having trouble remembering when a suitable <quote>good</quote> change -was, so that you can tell <command role="hg-cmd">hg bisect</command>, you could do worse than -testing changesets at random. Just remember to eliminate contenders -that can't possibly exhibit the bug (perhaps because the feature with -the bug isn't present yet) and those where another problem masks the -bug (as I discussed above). -</para> - -<para>Even if you end up <quote>early</quote> by thousands of changesets or months of -history, you will only add a handful of tests to the total number that -<command role="hg-cmd">hg bisect</command> must perform, thanks to its logarithmic behaviour. -</para> - -</sect2> -</sect1> +<chapter id="chap:undo"> + <?dbhtml filename="finding-and-fixing-mistakes.html"?> + <title>Finding and fixing mistakes</title> + + <para id="x_d2">To err might be human, but to really handle the consequences + well takes a top-notch revision control system. In this chapter, + we'll discuss some of the techniques you can use when you find + that a problem has crept into your project. Mercurial has some + highly capable features that will help you to isolate the sources + of problems, and to handle them appropriately.</para> + + <sect1> + <title>Erasing local history</title> + + <sect2> + <title>The accidental commit</title> + + <para id="x_d3">I have the occasional but persistent problem of typing + rather more quickly than I can think, which sometimes results + in me committing a changeset that is either incomplete or + plain wrong. In my case, the usual kind of incomplete + changeset is one in which I've created a new source file, but + forgotten to <command role="hg-cmd">hg add</command> it. A + <quote>plain wrong</quote> changeset is not as common, but no + less annoying.</para> + + </sect2> + <sect2 id="sec:undo:rollback"> + <title>Rolling back a transaction</title> + + <para id="x_d4">In <xref linkend="sec:concepts:txn"/>, I + mentioned that Mercurial treats each modification of a + repository as a <emphasis>transaction</emphasis>. Every time + you commit a changeset or pull changes from another + repository, Mercurial remembers what you did. You can undo, + or <emphasis>roll back</emphasis>, exactly one of these + actions using the <command role="hg-cmd">hg rollback</command> + command. (See <xref linkend="sec:undo:rollback-after-push"/> + for an important caveat about the use of this command.)</para> + + <para id="x_d5">Here's a mistake that I often find myself making: + committing a change in which I've created a new file, but + forgotten to <command role="hg-cmd">hg add</command> + it.</para> + + &interaction.rollback.commit; + + <para id="x_d6">Looking at the output of <command role="hg-cmd">hg + status</command> after the commit immediately confirms the + error.</para> + + &interaction.rollback.status; + + <para id="x_d7">The commit captured the changes to the file + <filename>a</filename>, but not the new file + <filename>b</filename>. If I were to push this changeset to a + repository that I shared with a colleague, the chances are + high that something in <filename>a</filename> would refer to + <filename>b</filename>, which would not be present in their + repository when they pulled my changes. I would thus become + the object of some indignation.</para> + + <para id="x_d8">However, luck is with me&emdash;I've caught my error + before I pushed the changeset. I use the <command + role="hg-cmd">hg rollback</command> command, and Mercurial + makes that last changeset vanish.</para> + + &interaction.rollback.rollback; + + <para id="x_d9">Notice that the changeset is no longer present in the + repository's history, and the working directory once again + thinks that the file <filename>a</filename> is modified. The + commit and rollback have left the working directory exactly as + it was prior to the commit; the changeset has been completely + erased. I can now safely <command role="hg-cmd">hg + add</command> the file <filename>b</filename>, and rerun my + commit.</para> + + &interaction.rollback.add; + + </sect2> + <sect2> + <title>The erroneous pull</title> + + <para id="x_da">It's common practice with Mercurial to maintain separate + development branches of a project in different repositories. + Your development team might have one shared repository for + your project's <quote>0.9</quote> release, and another, + containing different changes, for the <quote>1.0</quote> + release.</para> + + <para id="x_db">Given this, you can imagine that the consequences could be + messy if you had a local <quote>0.9</quote> repository, and + accidentally pulled changes from the shared <quote>1.0</quote> + repository into it. At worst, you could be paying + insufficient attention, and push those changes into the shared + <quote>0.9</quote> tree, confusing your entire team (but don't + worry, we'll return to this horror scenario later). However, + it's more likely that you'll notice immediately, because + Mercurial will display the URL it's pulling from, or you will + see it pull a suspiciously large number of changes into the + repository.</para> + + <para id="x_dc">The <command role="hg-cmd">hg rollback</command> command + will work nicely to expunge all of the changesets that you + just pulled. Mercurial groups all changes from one <command + role="hg-cmd">hg pull</command> into a single transaction, + so one <command role="hg-cmd">hg rollback</command> is all you + need to undo this mistake.</para> + + </sect2> + <sect2 id="sec:undo:rollback-after-push"> + <title>Rolling back is useless once you've pushed</title> + + <para id="x_dd">The value of the <command role="hg-cmd">hg + rollback</command> command drops to zero once you've pushed + your changes to another repository. Rolling back a change + makes it disappear entirely, but <emphasis>only</emphasis> in + the repository in which you perform the <command + role="hg-cmd">hg rollback</command>. Because a rollback + eliminates history, there's no way for the disappearance of a + change to propagate between repositories.</para> + + <para id="x_de">If you've pushed a change to another + repository&emdash;particularly if it's a shared + repository&emdash;it has essentially <quote>escaped into the + wild,</quote> and you'll have to recover from your mistake + in a different way. If you push a changeset somewhere, then + roll it back, then pull from the repository you pushed to, the + changeset you thought you'd gotten rid of will simply reappear + in your repository.</para> + + <para id="x_df">(If you absolutely know for sure that the change + you want to roll back is the most recent change in the + repository that you pushed to, <emphasis>and</emphasis> you + know that nobody else could have pulled it from that + repository, you can roll back the changeset there, too, but + you really should not expect this to work reliably. Sooner or + later a change really will make it into a repository that you + don't directly control (or have forgotten about), and come + back to bite you.)</para> + + </sect2> + <sect2> + <title>You can only roll back once</title> + + <para id="x_e0">Mercurial stores exactly one transaction in its + transaction log; that transaction is the most recent one that + occurred in the repository. This means that you can only roll + back one transaction. If you expect to be able to roll back + one transaction, then its predecessor, this is not the + behavior you will get.</para> + + &interaction.rollback.twice; + + <para id="x_e1">Once you've rolled back one transaction in a repository, + you can't roll back again in that repository until you perform + another commit or pull.</para> + + </sect2> + </sect1> + <sect1> + <title>Reverting the mistaken change</title> + + <para id="x_e2">If you make a modification to a file, and decide that you + really didn't want to change the file at all, and you haven't + yet committed your changes, the <command role="hg-cmd">hg + revert</command> command is the one you'll need. It looks at + the changeset that's the parent of the working directory, and + restores the contents of the file to their state as of that + changeset. (That's a long-winded way of saying that, in the + normal case, it undoes your modifications.)</para> + + <para id="x_e3">Let's illustrate how the <command role="hg-cmd">hg + revert</command> command works with yet another small example. + We'll begin by modifying a file that Mercurial is already + tracking.</para> + + &interaction.daily.revert.modify; + + <para id="x_e4">If we don't + want that change, we can simply <command role="hg-cmd">hg + revert</command> the file.</para> + + &interaction.daily.revert.unmodify; + + <para id="x_e5">The <command role="hg-cmd">hg revert</command> command + provides us with an extra degree of safety by saving our + modified file with a <filename>.orig</filename> + extension.</para> + + &interaction.daily.revert.status; + + <tip> + <title>Be careful with <filename>.orig</filename> files</title> + + <para id="x_6b8">It's extremely unlikely that you are either using + Mercurial to manage files with <filename>.orig</filename> + extensions or that you even care about the contents of such + files. Just in case, though, it's useful to remember that + <command role="hg-cmd">hg revert</command> will + unconditionally overwrite an existing file with a + <filename>.orig</filename> extension. For instance, if you + already have a file named <filename>foo.orig</filename> when + you revert <filename>foo</filename>, the contents of + <filename>foo.orig</filename> will be clobbered.</para> + </tip> + + <para id="x_e6">Here is a summary of the cases that the <command + role="hg-cmd">hg revert</command> command can deal with. We + will describe each of these in more detail in the section that + follows.</para> + <itemizedlist> + <listitem><para id="x_e7">If you modify a file, it will restore the file + to its unmodified state.</para> + </listitem> + <listitem><para id="x_e8">If you <command role="hg-cmd">hg add</command> a + file, it will undo the <quote>added</quote> state of the + file, but leave the file itself untouched.</para> + </listitem> + <listitem><para id="x_e9">If you delete a file without telling Mercurial, + it will restore the file to its unmodified contents.</para> + </listitem> + <listitem><para id="x_ea">If you use the <command role="hg-cmd">hg + remove</command> command to remove a file, it will undo + the <quote>removed</quote> state of the file, and restore + the file to its unmodified contents.</para> + </listitem></itemizedlist> + + <sect2 id="sec:undo:mgmt"> + <title>File management errors</title> + + <para id="x_eb">The <command role="hg-cmd">hg revert</command> command is + useful for more than just modified files. It lets you reverse + the results of all of Mercurial's file management + commands&emdash;<command role="hg-cmd">hg add</command>, + <command role="hg-cmd">hg remove</command>, and so on.</para> + + <para id="x_ec">If you <command role="hg-cmd">hg add</command> a file, + then decide that in fact you don't want Mercurial to track it, + use <command role="hg-cmd">hg revert</command> to undo the + add. Don't worry; Mercurial will not modify the file in any + way. It will just <quote>unmark</quote> the file.</para> + + &interaction.daily.revert.add; + + <para id="x_ed">Similarly, if you ask Mercurial to <command + role="hg-cmd">hg remove</command> a file, you can use + <command role="hg-cmd">hg revert</command> to restore it to + the contents it had as of the parent of the working directory. + &interaction.daily.revert.remove; This works just as + well for a file that you deleted by hand, without telling + Mercurial (recall that in Mercurial terminology, this kind of + file is called <quote>missing</quote>).</para> + + &interaction.daily.revert.missing; + + <para id="x_ee">If you revert a <command role="hg-cmd">hg copy</command>, + the copied-to file remains in your working directory + afterwards, untracked. Since a copy doesn't affect the + copied-from file in any way, Mercurial doesn't do anything + with the copied-from file.</para> + + &interaction.daily.revert.copy; + </sect2> + </sect1> + + <sect1> + <title>Dealing with committed changes</title> + + <para id="x_f5">Consider a case where you have committed a change + <emphasis>a</emphasis>, and another change + <emphasis>b</emphasis> on top of it; you then realise that + change <emphasis>a</emphasis> was incorrect. Mercurial lets you + <quote>back out</quote> an entire changeset automatically, and + building blocks that let you reverse part of a changeset by + hand.</para> + + <para id="x_f6">Before you read this section, here's something to + keep in mind: the <command role="hg-cmd">hg backout</command> + command undoes the effect of a change by + <emphasis>adding</emphasis> to your repository's history, not by + modifying or erasing it. It's the right tool to use if you're + fixing bugs, but not if you're trying to undo some change that + has catastrophic consequences. To deal with those, see + <xref linkend="sec:undo:aaaiiieee"/>.</para> + + <sect2> + <title>Backing out a changeset</title> + + <para id="x_f7">The <command role="hg-cmd">hg backout</command> command + lets you <quote>undo</quote> the effects of an entire + changeset in an automated fashion. Because Mercurial's + history is immutable, this command <emphasis>does + not</emphasis> get rid of the changeset you want to undo. + Instead, it creates a new changeset that + <emphasis>reverses</emphasis> the effect of the to-be-undone + changeset.</para> + + <para id="x_f8">The operation of the <command role="hg-cmd">hg + backout</command> command is a little intricate, so let's + illustrate it with some examples. First, we'll create a + repository with some simple changes.</para> + + &interaction.backout.init; + + <para id="x_f9">The <command role="hg-cmd">hg backout</command> command + takes a single changeset ID as its argument; this is the + changeset to back out. Normally, <command role="hg-cmd">hg + backout</command> will drop you into a text editor to write + a commit message, so you can record why you're backing the + change out. In this example, we provide a commit message on + the command line using the <option + role="hg-opt-backout">-m</option> option.</para> + + </sect2> + <sect2> + <title>Backing out the tip changeset</title> + + <para id="x_fa">We're going to start by backing out the last changeset we + committed.</para> + + &interaction.backout.simple; + + <para id="x_fb">You can see that the second line from + <filename>myfile</filename> is no longer present. Taking a + look at the output of <command role="hg-cmd">hg log</command> + gives us an idea of what the <command role="hg-cmd">hg + backout</command> command has done. + &interaction.backout.simple.log; Notice that the new changeset + that <command role="hg-cmd">hg backout</command> has created + is a child of the changeset we backed out. It's easier to see + this in <xref linkend="fig:undo:backout"/>, which presents a + graphical view of the change history. As you can see, the + history is nice and linear.</para> + + <figure id="fig:undo:backout"> + <title>Backing out a change using the <command + role="hg-cmd">hg backout</command> command</title> + <mediaobject> + <imageobject><imagedata fileref="figs/undo-simple.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + + </sect2> + <sect2> + <title>Backing out a non-tip change</title> + + <para id="x_fd">If you want to back out a change other than the last one + you committed, pass the <option + role="hg-opt-backout">--merge</option> option to the + <command role="hg-cmd">hg backout</command> command.</para> + + &interaction.backout.non-tip.clone; + + <para id="x_fe">This makes backing out any changeset a + <quote>one-shot</quote> operation that's usually simple and + fast.</para> + + &interaction.backout.non-tip.backout; + + <para id="x_ff">If you take a look at the contents of + <filename>myfile</filename> after the backout finishes, you'll + see that the first and third changes are present, but not the + second.</para> + + &interaction.backout.non-tip.cat; + + <para id="x_100">As the graphical history in <xref + linkend="fig:undo:backout-non-tip"/> illustrates, Mercurial + still commits one change in this kind of situation (the + box-shaped node is the ones that Mercurial commits + automatically), but the revision graph now looks different. + Before Mercurial begins the backout process, it first + remembers what the current parent of the working directory is. + It then backs out the target changeset, and commits that as a + changeset. Finally, it merges back to the previous parent of + the working directory, but notice that it <emphasis>does not + commit</emphasis> the result of the merge. The repository + now contains two heads, and the working directory is in a + merge state.</para> + + <figure id="fig:undo:backout-non-tip"> + <title>Automated backout of a non-tip change using the + <command role="hg-cmd">hg backout</command> command</title> + <mediaobject> + <imageobject><imagedata fileref="figs/undo-non-tip.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + + <para id="x_103">The result is that you end up <quote>back where you + were</quote>, only with some extra history that undoes the + effect of the changeset you wanted to back out.</para> + + <para id="x_6b9">You might wonder why Mercurial does not commit the result + of the merge that it performed. The reason lies in Mercurial + behaving conservatively: a merge naturally has more scope for + error than simply undoing the effect of the tip changeset, + so your work will be safest if you first inspect (and test!) + the result of the merge, <emphasis>then</emphasis> commit + it.</para> + + <sect3> + <title>Always use the <option + role="hg-opt-backout">--merge</option> option</title> + + <para id="x_104">In fact, since the <option + role="hg-opt-backout">--merge</option> option will do the + <quote>right thing</quote> whether or not the changeset + you're backing out is the tip (i.e. it won't try to merge if + it's backing out the tip, since there's no need), you should + <emphasis>always</emphasis> use this option when you run the + <command role="hg-cmd">hg backout</command> command.</para> + + </sect3> + </sect2> + <sect2> + <title>Gaining more control of the backout process</title> + + <para id="x_105">While I've recommended that you always use the <option + role="hg-opt-backout">--merge</option> option when backing + out a change, the <command role="hg-cmd">hg backout</command> + command lets you decide how to merge a backout changeset. + Taking control of the backout process by hand is something you + will rarely need to do, but it can be useful to understand + what the <command role="hg-cmd">hg backout</command> command + is doing for you automatically. To illustrate this, let's + clone our first repository, but omit the backout change that + it contains.</para> + + &interaction.backout.manual.clone; + + <para id="x_106">As with our + earlier example, We'll commit a third changeset, then back out + its parent, and see what happens.</para> + + &interaction.backout.manual.backout; + + <para id="x_107">Our new changeset is again a descendant of the changeset + we backout out; it's thus a new head, <emphasis>not</emphasis> + a descendant of the changeset that was the tip. The <command + role="hg-cmd">hg backout</command> command was quite + explicit in telling us this.</para> + + &interaction.backout.manual.log; + + <para id="x_108">Again, it's easier to see what has happened by looking at + a graph of the revision history, in <xref + linkend="fig:undo:backout-manual"/>. This makes it clear + that when we use <command role="hg-cmd">hg backout</command> + to back out a change other than the tip, Mercurial adds a new + head to the repository (the change it committed is + box-shaped).</para> + + <figure id="fig:undo:backout-manual"> + <title>Backing out a change using the <command + role="hg-cmd">hg backout</command> command</title> + <mediaobject> + <imageobject><imagedata fileref="figs/undo-manual.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + + <para id="x_10a">After the <command role="hg-cmd">hg backout</command> + command has completed, it leaves the new + <quote>backout</quote> changeset as the parent of the working + directory.</para> + + &interaction.backout.manual.parents; + + <para id="x_10b">Now we have two isolated sets of changes.</para> + + &interaction.backout.manual.heads; + + <para id="x_10c">Let's think about what we expect to see as the contents of + <filename>myfile</filename> now. The first change should be + present, because we've never backed it out. The second change + should be missing, as that's the change we backed out. Since + the history graph shows the third change as a separate head, + we <emphasis>don't</emphasis> expect to see the third change + present in <filename>myfile</filename>.</para> + + &interaction.backout.manual.cat; + + <para id="x_10d">To get the third change back into the file, we just do a + normal merge of our two heads.</para> + + &interaction.backout.manual.merge; + + <para id="x_10e">Afterwards, the graphical history of our + repository looks like + <xref linkend="fig:undo:backout-manual-merge"/>.</para> + + <figure id="fig:undo:backout-manual-merge"> + <title>Manually merging a backout change</title> + <mediaobject> + <imageobject><imagedata fileref="figs/undo-manual-merge.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + + </sect2> + <sect2> + <title>Why <command role="hg-cmd">hg backout</command> works as + it does</title> + + <para id="x_110">Here's a brief description of how the <command + role="hg-cmd">hg backout</command> command works.</para> + <orderedlist> + <listitem><para id="x_111">It ensures that the working directory is + <quote>clean</quote>, i.e. that the output of <command + role="hg-cmd">hg status</command> would be empty.</para> + </listitem> + <listitem><para id="x_112">It remembers the current parent of the working + directory. Let's call this changeset + <literal>orig</literal>.</para> + </listitem> + <listitem><para id="x_113">It does the equivalent of a <command + role="hg-cmd">hg update</command> to sync the working + directory to the changeset you want to back out. Let's + call this changeset <literal>backout</literal>.</para> + </listitem> + <listitem><para id="x_114">It finds the parent of that changeset. Let's + call that changeset <literal>parent</literal>.</para> + </listitem> + <listitem><para id="x_115">For each file that the + <literal>backout</literal> changeset affected, it does the + equivalent of a <command role="hg-cmd">hg revert -r + parent</command> on that file, to restore it to the + contents it had before that changeset was + committed.</para> + </listitem> + <listitem><para id="x_116">It commits the result as a new changeset. + This changeset has <literal>backout</literal> as its + parent.</para> + </listitem> + <listitem><para id="x_117">If you specify <option + role="hg-opt-backout">--merge</option> on the command + line, it merges with <literal>orig</literal>, and commits + the result of the merge.</para> + </listitem></orderedlist> + + <para id="x_118">An alternative way to implement the <command + role="hg-cmd">hg backout</command> command would be to + <command role="hg-cmd">hg export</command> the + to-be-backed-out changeset as a diff, then use the <option + role="cmd-opt-patch">--reverse</option> option to the + <command>patch</command> command to reverse the effect of the + change without fiddling with the working directory. This + sounds much simpler, but it would not work nearly as + well.</para> + + <para id="x_119">The reason that <command role="hg-cmd">hg + backout</command> does an update, a commit, a merge, and + another commit is to give the merge machinery the best chance + to do a good job when dealing with all the changes + <emphasis>between</emphasis> the change you're backing out and + the current tip.</para> + + <para id="x_11a">If you're backing out a changeset that's 100 revisions + back in your project's history, the chances that the + <command>patch</command> command will be able to apply a + reverse diff cleanly are not good, because intervening changes + are likely to have <quote>broken the context</quote> that + <command>patch</command> uses to determine whether it can + apply a patch (if this sounds like gibberish, see <xref + linkend="sec:mq:patch"/> for a + discussion of the <command>patch</command> command). Also, + Mercurial's merge machinery will handle files and directories + being renamed, permission changes, and modifications to binary + files, none of which <command>patch</command> can deal + with.</para> + + </sect2> + </sect1> + <sect1 id="sec:undo:aaaiiieee"> + <title>Changes that should never have been</title> + + <para id="x_11b">Most of the time, the <command role="hg-cmd">hg + backout</command> command is exactly what you need if you want + to undo the effects of a change. It leaves a permanent record + of exactly what you did, both when committing the original + changeset and when you cleaned up after it.</para> + + <para id="x_11c">On rare occasions, though, you may find that you've + committed a change that really should not be present in the + repository at all. For example, it would be very unusual, and + usually considered a mistake, to commit a software project's + object files as well as its source files. Object files have + almost no intrinsic value, and they're <emphasis>big</emphasis>, + so they increase the size of the repository and the amount of + time it takes to clone or pull changes.</para> + + <para id="x_11d">Before I discuss the options that you have if you commit a + <quote>brown paper bag</quote> change (the kind that's so bad + that you want to pull a brown paper bag over your head), let me + first discuss some approaches that probably won't work.</para> + + <para id="x_11e">Since Mercurial treats history as + accumulative&emdash;every change builds on top of all changes + that preceded it&emdash;you generally can't just make disastrous + changes disappear. The one exception is when you've just + committed a change, and it hasn't been pushed or pulled into + another repository. That's when you can safely use the <command + role="hg-cmd">hg rollback</command> command, as I detailed in + <xref linkend="sec:undo:rollback"/>.</para> + + <para id="x_11f">After you've pushed a bad change to another repository, you + <emphasis>could</emphasis> still use <command role="hg-cmd">hg + rollback</command> to make your local copy of the change + disappear, but it won't have the consequences you want. The + change will still be present in the remote repository, so it + will reappear in your local repository the next time you + pull.</para> + + <para id="x_120">If a situation like this arises, and you know which + repositories your bad change has propagated into, you can + <emphasis>try</emphasis> to get rid of the change from + <emphasis>every</emphasis> one of those repositories. This is, + of course, not a satisfactory solution: if you miss even a + single repository while you're expunging, the change is still + <quote>in the wild</quote>, and could propagate further.</para> + + <para id="x_121">If you've committed one or more changes + <emphasis>after</emphasis> the change that you'd like to see + disappear, your options are further reduced. Mercurial doesn't + provide a way to <quote>punch a hole</quote> in history, leaving + changesets intact.</para> + + <sect2> + <title>Backing out a merge</title> + + <para id="x_6ba">Since merges are often complicated, it is not unheard of + for a merge to be mangled badly, but committed erroneously. + Mercurial provides an important safeguard against bad merges + by refusing to commit unresolved files, but human ingenuity + guarantees that it is still possible to mess a merge up and + commit it.</para> + + <para id="x_6bb">Given a bad merge that has been committed, usually the + best way to approach it is to simply try to repair the damage + by hand. A complete disaster that cannot be easily fixed up + by hand ought to be very rare, but the <command + role="hg-cmd">hg backout</command> command may help in + making the cleanup easier. It offers a <option + role="hg-opt-backout">--parent</option> option, which lets + you specify which parent to revert to when backing out a + merge.</para> + + <figure id="fig:undo:bad-merge-1"> + <title>A bad merge</title> + <mediaobject> + <imageobject><imagedata fileref="figs/bad-merge-1.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + + <para id="x_6bc">Suppose we have a revision graph like that in <xref + linkend="fig:undo:bad-merge-1"/>. What we'd like is to + <emphasis>redo</emphasis> the merge of revisions 2 and + 3.</para> + + <para id="x_6bd">One way to do so would be as follows.</para> + + <orderedlist> + <listitem> + <para id="x_6be">Call <command role="hg-cmd">hg backout --rev=4 + --parent=2</command>. This tells <command + role="hg-cmd">hg backout</command> to back out revision + 4, which is the bad merge, and to when deciding which + revision to prefer, to choose parent 2, one of the parents + of the merge. The effect can be seen in <xref + linkend="fig:undo:bad-merge-2"/>.</para> + <figure id="fig:undo:bad-merge-2"> + <title>Backing out the merge, favoring one parent</title> + <mediaobject> + <imageobject><imagedata fileref="figs/bad-merge-2.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + </listitem> + + <listitem> + <para id="x_6bf">Call <command role="hg-cmd">hg backout --rev=4 + --parent=3</command>. This tells <command + role="hg-cmd">hg backout</command> to back out revision + 4 again, but this time to choose parent 3, the other + parent of the merge. The result is visible in <xref + linkend="fig:undo:bad-merge-3"/>, in which the repository + now contains three heads.</para> + <figure id="fig:undo:bad-merge-3"> + <title>Backing out the merge, favoring the other + parent</title> + <mediaobject> + <imageobject><imagedata fileref="figs/bad-merge-3.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + </listitem> + + <listitem> + <para id="x_6c0">Redo the bad merge by merging the two backout heads, + which reduces the number of heads in the repository to + two, as can be seen in <xref + linkend="fig:undo:bad-merge-4"/>.</para> + <figure id="fig:undo:bad-merge-4"> + <title>Merging the backouts</title> + <mediaobject> + <imageobject><imagedata fileref="figs/bad-merge-4.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + </listitem> + + <listitem> + <para id="x_6c1">Merge with the commit that was made after the bad + merge, as shown in <xref + linkend="fig:undo:bad-merge-5"/>.</para> + <figure id="fig:undo:bad-merge-5"> + <title>Merging the backouts</title> + <mediaobject> + <imageobject><imagedata fileref="figs/bad-merge-5.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + </listitem> + </orderedlist> + </sect2> + + <sect2> + <title>Protect yourself from <quote>escaped</quote> + changes</title> + + <para id="x_123">If you've committed some changes to your local repository + and they've been pushed or pulled somewhere else, this isn't + necessarily a disaster. You can protect yourself ahead of + time against some classes of bad changeset. This is + particularly easy if your team usually pulls changes from a + central repository.</para> + + <para id="x_124">By configuring some hooks on that repository to validate + incoming changesets (see chapter <xref linkend="chap:hook"/>), + you can + automatically prevent some kinds of bad changeset from being + pushed to the central repository at all. With such a + configuration in place, some kinds of bad changeset will + naturally tend to <quote>die out</quote> because they can't + propagate into the central repository. Better yet, this + happens without any need for explicit intervention.</para> + + <para id="x_125">For instance, an incoming change hook that + verifies that a changeset will actually compile can prevent + people from inadvertently <quote>breaking the + build</quote>.</para> + </sect2> + + <sect2> + <title>What to do about sensitive changes that escape</title> + + <para id="x_6c2">Even a carefully run project can suffer an unfortunate + event such as the committing and uncontrolled propagation of a + file that contains important passwords.</para> + + <para id="x_6c3">If something like this happens to you, and the information + that gets accidentally propagated is truly sensitive, your + first step should be to mitigate the effect of the leak + without trying to control the leak itself. If you are not 100% + certain that you know exactly who could have seen the changes, + you should immediately change passwords, cancel credit cards, + or find some other way to make sure that the information that + has leaked is no longer useful. In other words, assume that + the change has propagated far and wide, and that there's + nothing more you can do.</para> + + <para id="x_6c4">You might hope that there would be mechanisms you could + use to either figure out who has seen a change or to erase the + change permanently everywhere, but there are good reasons why + these are not possible.</para> + + <para id="x_6c5">Mercurial does not provide an audit trail of who has + pulled changes from a repository, because it is usually either + impossible to record such information or trivial to spoof it. + In a multi-user or networked environment, you should thus be + extremely skeptical of yourself if you think that you have + identified every place that a sensitive changeset has + propagated to. Don't forget that people can and will send + bundles by email, have their backup software save data + offsite, carry repositories on USB sticks, and find other + completely innocent ways to confound your attempts to track + down every copy of a problematic change.</para> + + <para id="x_6c6">Mercurial also does not provide a way to make a file or + changeset completely disappear from history, because there is + no way to enforce its disappearance; someone could easily + modify their copy of Mercurial to ignore such directives. In + addition, even if Mercurial provided such a capability, + someone who simply hadn't pulled a <quote>make this file + disappear</quote> changeset wouldn't be affected by it, nor + would web crawlers visiting at the wrong time, disk backups, + or other mechanisms. Indeed, no distributed revision control + system can make data reliably vanish. Providing the illusion + of such control could easily give a false sense of security, + and be worse than not providing it at all.</para> + </sect2> + </sect1> + + <sect1 id="sec:undo:bisect"> + <title>Finding the source of a bug</title> + + <para id="x_126">While it's all very well to be able to back out a changeset + that introduced a bug, this requires that you know which + changeset to back out. Mercurial provides an invaluable + command, called <command role="hg-cmd">hg bisect</command>, that + helps you to automate this process and accomplish it very + efficiently.</para> + + <para id="x_127">The idea behind the <command role="hg-cmd">hg + bisect</command> command is that a changeset has introduced + some change of behavior that you can identify with a simple + pass/fail test. You don't know which piece of code introduced the + change, but you know how to test for the presence of the bug. + The <command role="hg-cmd">hg bisect</command> command uses your + test to direct its search for the changeset that introduced the + code that caused the bug.</para> + + <para id="x_128">Here are a few scenarios to help you understand how you + might apply this command.</para> + <itemizedlist> + <listitem><para id="x_129">The most recent version of your software has a + bug that you remember wasn't present a few weeks ago, but + you don't know when it was introduced. Here, your binary + test checks for the presence of that bug.</para> + </listitem> + <listitem><para id="x_12a">You fixed a bug in a rush, and now it's time to + close the entry in your team's bug database. The bug + database requires a changeset ID when you close an entry, + but you don't remember which changeset you fixed the bug in. + Once again, your binary test checks for the presence of the + bug.</para> + </listitem> + <listitem><para id="x_12b">Your software works correctly, but runs 15% + slower than the last time you measured it. You want to know + which changeset introduced the performance regression. In + this case, your binary test measures the performance of your + software, to see whether it's <quote>fast</quote> or + <quote>slow</quote>.</para> + </listitem> + <listitem><para id="x_12c">The sizes of the components of your project that + you ship exploded recently, and you suspect that something + changed in the way you build your project.</para> + </listitem></itemizedlist> + + <para id="x_12d">From these examples, it should be clear that the <command + role="hg-cmd">hg bisect</command> command is not useful only + for finding the sources of bugs. You can use it to find any + <quote>emergent property</quote> of a repository (anything that + you can't find from a simple text search of the files in the + tree) for which you can write a binary test.</para> + + <para id="x_12e">We'll introduce a little bit of terminology here, just to + make it clear which parts of the search process are your + responsibility, and which are Mercurial's. A + <emphasis>test</emphasis> is something that + <emphasis>you</emphasis> run when <command role="hg-cmd">hg + bisect</command> chooses a changeset. A + <emphasis>probe</emphasis> is what <command role="hg-cmd">hg + bisect</command> runs to tell whether a revision is good. + Finally, we'll use the word <quote>bisect</quote>, as both a + noun and a verb, to stand in for the phrase <quote>search using + the <command role="hg-cmd">hg bisect</command> + command</quote>.</para> + + <para id="x_12f">One simple way to automate the searching process would be + simply to probe every changeset. However, this scales poorly. + If it took ten minutes to test a single changeset, and you had + 10,000 changesets in your repository, the exhaustive approach + would take on average 35 <emphasis>days</emphasis> to find the + changeset that introduced a bug. Even if you knew that the bug + was introduced by one of the last 500 changesets, and limited + your search to those, you'd still be looking at over 40 hours to + find the changeset that introduced your bug.</para> + + <para id="x_130">What the <command role="hg-cmd">hg bisect</command> command + does is use its knowledge of the <quote>shape</quote> of your + project's revision history to perform a search in time + proportional to the <emphasis>logarithm</emphasis> of the number + of changesets to check (the kind of search it performs is called + a dichotomic search). With this approach, searching through + 10,000 changesets will take less than three hours, even at ten + minutes per test (the search will require about 14 tests). + Limit your search to the last hundred changesets, and it will + take only about an hour (roughly seven tests).</para> + + <para id="x_131">The <command role="hg-cmd">hg bisect</command> command is + aware of the <quote>branchy</quote> nature of a Mercurial + project's revision history, so it has no problems dealing with + branches, merges, or multiple heads in a repository. It can + prune entire branches of history with a single probe, which is + how it operates so efficiently.</para> + + <sect2> + <title>Using the <command role="hg-cmd">hg bisect</command> + command</title> + + <para id="x_132">Here's an example of <command role="hg-cmd">hg + bisect</command> in action.</para> + + <note> + <para id="x_133"> In versions 0.9.5 and earlier of Mercurial, <command + role="hg-cmd">hg bisect</command> was not a core command: + it was distributed with Mercurial as an extension. This + section describes the built-in command, not the old + extension.</para> + </note> + + <para id="x_134">Now let's create a repository, so that we can try out the + <command role="hg-cmd">hg bisect</command> command in + isolation.</para> + + &interaction.bisect.init; + + <para id="x_135">We'll simulate a project that has a bug in it in a + simple-minded way: create trivial changes in a loop, and + nominate one specific change that will have the + <quote>bug</quote>. This loop creates 35 changesets, each + adding a single file to the repository. We'll represent our + <quote>bug</quote> with a file that contains the text <quote>i + have a gub</quote>.</para> + + &interaction.bisect.commits; + + <para id="x_136">The next thing that we'd like to do is figure out how to + use the <command role="hg-cmd">hg bisect</command> command. + We can use Mercurial's normal built-in help mechanism for + this.</para> + + &interaction.bisect.help; + + <para id="x_137">The <command role="hg-cmd">hg bisect</command> command + works in steps. Each step proceeds as follows.</para> + <orderedlist> + <listitem><para id="x_138">You run your binary test.</para> + <itemizedlist> + <listitem><para id="x_139">If the test succeeded, you tell <command + role="hg-cmd">hg bisect</command> by running the + <command role="hg-cmd">hg bisect --good</command> + command.</para> + </listitem> + <listitem><para id="x_13a">If it failed, run the <command + role="hg-cmd">hg bisect --bad</command> + command.</para></listitem></itemizedlist> + </listitem> + <listitem><para id="x_13b">The command uses your information to decide + which changeset to test next.</para> + </listitem> + <listitem><para id="x_13c">It updates the working directory to that + changeset, and the process begins again.</para> + </listitem></orderedlist> + <para id="x_13d">The process ends when <command role="hg-cmd">hg + bisect</command> identifies a unique changeset that marks + the point where your test transitioned from + <quote>succeeding</quote> to <quote>failing</quote>.</para> + + <para id="x_13e">To start the search, we must run the <command + role="hg-cmd">hg bisect --reset</command> command.</para> + + &interaction.bisect.search.init; + + <para id="x_13f">In our case, the binary test we use is simple: we check to + see if any file in the repository contains the string <quote>i + have a gub</quote>. If it does, this changeset contains the + change that <quote>caused the bug</quote>. By convention, a + changeset that has the property we're searching for is + <quote>bad</quote>, while one that doesn't is + <quote>good</quote>.</para> + + <para id="x_140">Most of the time, the revision to which the working + directory is synced (usually the tip) already exhibits the + problem introduced by the buggy change, so we'll mark it as + <quote>bad</quote>.</para> + + &interaction.bisect.search.bad-init; + + <para id="x_141">Our next task is to nominate a changeset that we know + <emphasis>doesn't</emphasis> have the bug; the <command + role="hg-cmd">hg bisect</command> command will + <quote>bracket</quote> its search between the first pair of + good and bad changesets. In our case, we know that revision + 10 didn't have the bug. (I'll have more words about choosing + the first <quote>good</quote> changeset later.)</para> + + &interaction.bisect.search.good-init; + + <para id="x_142">Notice that this command printed some output.</para> + <itemizedlist> + <listitem><para id="x_143">It told us how many changesets it must + consider before it can identify the one that introduced + the bug, and how many tests that will require.</para> + </listitem> + <listitem><para id="x_144">It updated the working directory to the next + changeset to test, and told us which changeset it's + testing.</para> + </listitem></itemizedlist> + + <para id="x_145">We now run our test in the working directory. We use the + <command>grep</command> command to see if our + <quote>bad</quote> file is present in the working directory. + If it is, this revision is bad; if not, this revision is good. + &interaction.bisect.search.step1;</para> + + <para id="x_146">This test looks like a perfect candidate for automation, + so let's turn it into a shell function.</para> + &interaction.bisect.search.mytest; + + <para id="x_147">We can now run an entire test step with a single command, + <literal>mytest</literal>.</para> + + &interaction.bisect.search.step2; + + <para id="x_148">A few more invocations of our canned test step command, + and we're done.</para> + + &interaction.bisect.search.rest; + + <para id="x_149">Even though we had 40 changesets to search through, the + <command role="hg-cmd">hg bisect</command> command let us find + the changeset that introduced our <quote>bug</quote> with only + five tests. Because the number of tests that the <command + role="hg-cmd">hg bisect</command> command performs grows + logarithmically with the number of changesets to search, the + advantage that it has over the <quote>brute force</quote> + search approach increases with every changeset you add.</para> + + </sect2> + <sect2> + <title>Cleaning up after your search</title> + + <para id="x_14a">When you're finished using the <command role="hg-cmd">hg + bisect</command> command in a repository, you can use the + <command role="hg-cmd">hg bisect --reset</command> command to + drop the information it was using to drive your search. The + command doesn't use much space, so it doesn't matter if you + forget to run this command. However, <command + role="hg-cmd">hg bisect</command> won't let you start a new + search in that repository until you do a <command + role="hg-cmd">hg bisect --reset</command>.</para> + + &interaction.bisect.search.reset; + + </sect2> + </sect1> + <sect1> + <title>Tips for finding bugs effectively</title> + + <sect2> + <title>Give consistent input</title> + + <para id="x_14b">The <command role="hg-cmd">hg bisect</command> command + requires that you correctly report the result of every test + you perform. If you tell it that a test failed when it really + succeeded, it <emphasis>might</emphasis> be able to detect the + inconsistency. If it can identify an inconsistency in your + reports, it will tell you that a particular changeset is both + good and bad. However, it can't do this perfectly; it's about + as likely to report the wrong changeset as the source of the + bug.</para> + + </sect2> + <sect2> + <title>Automate as much as possible</title> + + <para id="x_14c">When I started using the <command role="hg-cmd">hg + bisect</command> command, I tried a few times to run my + tests by hand, on the command line. This is an approach that + I, at least, am not suited to. After a few tries, I found + that I was making enough mistakes that I was having to restart + my searches several times before finally getting correct + results.</para> + + <para id="x_14d">My initial problems with driving the <command + role="hg-cmd">hg bisect</command> command by hand occurred + even with simple searches on small repositories; if the + problem you're looking for is more subtle, or the number of + tests that <command role="hg-cmd">hg bisect</command> must + perform increases, the likelihood of operator error ruining + the search is much higher. Once I started automating my + tests, I had much better results.</para> + + <para id="x_14e">The key to automated testing is twofold:</para> + <itemizedlist> + <listitem><para id="x_14f">always test for the same symptom, and</para> + </listitem> + <listitem><para id="x_150">always feed consistent input to the <command + role="hg-cmd">hg bisect</command> command.</para> + </listitem></itemizedlist> + <para id="x_151">In my tutorial example above, the <command>grep</command> + command tests for the symptom, and the <literal>if</literal> + statement takes the result of this check and ensures that we + always feed the same input to the <command role="hg-cmd">hg + bisect</command> command. The <literal>mytest</literal> + function marries these together in a reproducible way, so that + every test is uniform and consistent.</para> + + </sect2> + <sect2> + <title>Check your results</title> + + <para id="x_152">Because the output of a <command role="hg-cmd">hg + bisect</command> search is only as good as the input you + give it, don't take the changeset it reports as the absolute + truth. A simple way to cross-check its report is to manually + run your test at each of the following changesets:</para> + <itemizedlist> + <listitem><para id="x_153">The changeset that it reports as the first bad + revision. Your test should still report this as + bad.</para> + </listitem> + <listitem><para id="x_154">The parent of that changeset (either parent, + if it's a merge). Your test should report this changeset + as good.</para> + </listitem> + <listitem><para id="x_155">A child of that changeset. Your test should + report this changeset as bad.</para> + </listitem></itemizedlist> + + </sect2> + <sect2> + <title>Beware interference between bugs</title> + + <para id="x_156">It's possible that your search for one bug could be + disrupted by the presence of another. For example, let's say + your software crashes at revision 100, and worked correctly at + revision 50. Unknown to you, someone else introduced a + different crashing bug at revision 60, and fixed it at + revision 80. This could distort your results in one of + several ways.</para> + + <para id="x_157">It is possible that this other bug completely + <quote>masks</quote> yours, which is to say that it occurs + before your bug has a chance to manifest itself. If you can't + avoid that other bug (for example, it prevents your project + from building), and so can't tell whether your bug is present + in a particular changeset, the <command role="hg-cmd">hg + bisect</command> command cannot help you directly. Instead, + you can mark a changeset as untested by running <command + role="hg-cmd">hg bisect --skip</command>.</para> + + <para id="x_158">A different problem could arise if your test for a bug's + presence is not specific enough. If you check for <quote>my + program crashes</quote>, then both your crashing bug and an + unrelated crashing bug that masks it will look like the same + thing, and mislead <command role="hg-cmd">hg + bisect</command>.</para> + + <para id="x_159">Another useful situation in which to use <command + role="hg-cmd">hg bisect --skip</command> is if you can't + test a revision because your project was in a broken and hence + untestable state at that revision, perhaps because someone + checked in a change that prevented the project from + building.</para> + + </sect2> + <sect2> + <title>Bracket your search lazily</title> + + <para id="x_15a">Choosing the first <quote>good</quote> and + <quote>bad</quote> changesets that will mark the end points of + your search is often easy, but it bears a little discussion + nevertheless. From the perspective of <command + role="hg-cmd">hg bisect</command>, the <quote>newest</quote> + changeset is conventionally <quote>bad</quote>, and the older + changeset is <quote>good</quote>.</para> + + <para id="x_15b">If you're having trouble remembering when a suitable + <quote>good</quote> change was, so that you can tell <command + role="hg-cmd">hg bisect</command>, you could do worse than + testing changesets at random. Just remember to eliminate + contenders that can't possibly exhibit the bug (perhaps + because the feature with the bug isn't present yet) and those + where another problem masks the bug (as I discussed + above).</para> + + <para id="x_15c">Even if you end up <quote>early</quote> by thousands of + changesets or months of history, you will only add a handful + of tests to the total number that <command role="hg-cmd">hg + bisect</command> must perform, thanks to its logarithmic + behavior.</para> + + </sect2> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch10-hook.xml --- a/fr/ch10-hook.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch10-hook.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,1883 +1,1928 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Handling repository events with hooks</title> -<para>\label{chap:hook}</para> - -<para>Mercurial offers a powerful mechanism to let you perform automated -actions in response to events that occur in a repository. In some -cases, you can even control Mercurial's response to those events.</para> - -<para>The name Mercurial uses for one of these actions is a <emphasis>hook</emphasis>. -Hooks are called <quote>triggers</quote> in some revision control systems, but -the two names refer to the same idea.</para> - -<sect1> -<title>An overview of hooks in Mercurial</title> - -<para>Here is a brief list of the hooks that Mercurial supports. We will -revisit each of these hooks in more detail later, in -section <xref linkend="sec:hook:ref"/>.</para> - -<itemizedlist> -<listitem><para><literal role="hook">changegroup</literal>: This is run after a group of - changesets has been brought into the repository from elsewhere.</para> -</listitem> -<listitem><para><literal role="hook">commit</literal>: This is run after a new changeset has been - created in the local repository.</para> -</listitem> -<listitem><para><literal role="hook">incoming</literal>: This is run once for each new changeset - that is brought into the repository from elsewhere. Notice the - difference from <literal role="hook">changegroup</literal>, which is run once per - <emphasis>group</emphasis> of changesets brought in.</para> -</listitem> -<listitem><para><literal role="hook">outgoing</literal>: This is run after a group of changesets - has been transmitted from this repository.</para> -</listitem> -<listitem><para><literal role="hook">prechangegroup</literal>: This is run before starting to - bring a group of changesets into the repository. -</para> -</listitem> -<listitem><para><literal role="hook">precommit</literal>: Controlling. This is run before starting - a commit. -</para> -</listitem> -<listitem><para><literal role="hook">preoutgoing</literal>: Controlling. This is run before - starting to transmit a group of changesets from this repository. -</para> -</listitem> -<listitem><para><literal role="hook">pretag</literal>: Controlling. This is run before creating a tag. -</para> -</listitem> -<listitem><para><literal role="hook">pretxnchangegroup</literal>: Controlling. This is run after a - group of changesets has been brought into the local repository from - another, but before the transaction completes that will make the - changes permanent in the repository. -</para> -</listitem> -<listitem><para><literal role="hook">pretxncommit</literal>: Controlling. This is run after a new - changeset has been created in the local repository, but before the - transaction completes that will make it permanent. -</para> -</listitem> -<listitem><para><literal role="hook">preupdate</literal>: Controlling. This is run before starting - an update or merge of the working directory. -</para> -</listitem> -<listitem><para><literal role="hook">tag</literal>: This is run after a tag is created. -</para> -</listitem> -<listitem><para><literal role="hook">update</literal>: This is run after an update or merge of the - working directory has finished. -</para> -</listitem></itemizedlist> -<para>Each of the hooks whose description begins with the word -<quote>Controlling</quote> has the ability to determine whether an activity can -proceed. If the hook succeeds, the activity may proceed; if it fails, -the activity is either not permitted or undone, depending on the hook. -</para> - -</sect1> -<sect1> -<title>Hooks and security</title> - -<sect2> -<title>Hooks are run with your privileges</title> - -<para>When you run a Mercurial command in a repository, and the command -causes a hook to run, that hook runs on <emphasis>your</emphasis> system, under -<emphasis>your</emphasis> user account, with <emphasis>your</emphasis> privilege level. Since -hooks are arbitrary pieces of executable code, you should treat them -with an appropriate level of suspicion. Do not install a hook unless -you are confident that you know who created it and what it does. -</para> - -<para>In some cases, you may be exposed to hooks that you did not install -yourself. If you work with Mercurial on an unfamiliar system, -Mercurial will run hooks defined in that system's global <filename role="special"> /.hgrc</filename>\ file. -</para> - -<para>If you are working with a repository owned by another user, Mercurial -can run hooks defined in that user's repository, but it will still run -them as <quote>you</quote>. For example, if you <command role="hg-cmd">hg pull</command> from that -repository, and its <filename role="special">.hg/hgrc</filename> defines a local -<literal role="hook">outgoing</literal> hook, that hook will run under your user account, even -though you don't own that repository. -</para> - -<note> -<para> This only applies if you are pulling from a repository on a local or - network filesystem. If you're pulling over http or ssh, any - <literal role="hook">outgoing</literal> hook will run under whatever account is executing - the server process, on the server. -</para> -</note> - -<para>XXX To see what hooks are defined in a repository, use the -<command role="hg-cmd">hg config hooks</command> command. If you are working in one -repository, but talking to another that you do not own (e.g. using -<command role="hg-cmd">hg pull</command> or <command role="hg-cmd">hg incoming</command>), remember that it is the other -repository's hooks you should be checking, not your own. -</para> - -</sect2> -<sect2> -<title>Hooks do not propagate</title> - -<para>In Mercurial, hooks are not revision controlled, and do not propagate -when you clone, or pull from, a repository. The reason for this is -simple: a hook is a completely arbitrary piece of executable code. It -runs under your user identity, with your privilege level, on your -machine. -</para> - -<para>It would be extremely reckless for any distributed revision control -system to implement revision-controlled hooks, as this would offer an -easily exploitable way to subvert the accounts of users of the -revision control system. -</para> - -<para>Since Mercurial does not propagate hooks, if you are collaborating -with other people on a common project, you should not assume that they -are using the same Mercurial hooks as you are, or that theirs are -correctly configured. You should document the hooks you expect people -to use. -</para> - -<para>In a corporate intranet, this is somewhat easier to control, as you -can for example provide a <quote>standard</quote> installation of Mercurial on an -NFS filesystem, and use a site-wide <filename role="special"> /.hgrc</filename>\ file to define hooks that -all users will see. However, this too has its limits; see below. -</para> - -</sect2> -<sect2> -<title>Hooks can be overridden</title> - -<para>Mercurial allows you to override a hook definition by redefining the -hook. You can disable it by setting its value to the empty string, or -change its behaviour as you wish. -</para> - -<para>If you deploy a system- or site-wide <filename role="special"> /.hgrc</filename>\ file that defines some -hooks, you should thus understand that your users can disable or -override those hooks. -</para> - -</sect2> -<sect2> -<title>Ensuring that critical hooks are run</title> - -<para>Sometimes you may want to enforce a policy that you do not want others -to be able to work around. For example, you may have a requirement -that every changeset must pass a rigorous set of tests. Defining this -requirement via a hook in a site-wide <filename role="special"> /.hgrc</filename>\ won't work for remote -users on laptops, and of course local users can subvert it at will by -overriding the hook. -</para> - -<para>Instead, you can set up your policies for use of Mercurial so that -people are expected to propagate changes through a well-known -<quote>canonical</quote> server that you have locked down and configured -appropriately. -</para> - -<para>One way to do this is via a combination of social engineering and -technology. Set up a restricted-access account; users can push -changes over the network to repositories managed by this account, but -they cannot log into the account and run normal shell commands. In -this scenario, a user can commit a changeset that contains any old -garbage they want. -</para> - -<para>When someone pushes a changeset to the server that everyone pulls -from, the server will test the changeset before it accepts it as -permanent, and reject it if it fails to pass the test suite. If -people only pull changes from this filtering server, it will serve to -ensure that all changes that people pull have been automatically -vetted. -</para> - -</sect2> -</sect1> -<sect1> -<title>Care with <literal>pretxn</literal> hooks in a shared-access repository</title> - -<para>If you want to use hooks to do some automated work in a repository -that a number of people have shared access to, you need to be careful -in how you do this. -</para> - -<para>Mercurial only locks a repository when it is writing to the -repository, and only the parts of Mercurial that write to the -repository pay attention to locks. Write locks are necessary to -prevent multiple simultaneous writers from scribbling on each other's -work, corrupting the repository. -</para> - -<para>Because Mercurial is careful with the order in which it reads and -writes data, it does not need to acquire a lock when it wants to read -data from the repository. The parts of Mercurial that read from the -repository never pay attention to locks. This lockless reading scheme -greatly increases performance and concurrency. -</para> - -<para>With great performance comes a trade-off, though, one which has the -potential to cause you trouble unless you're aware of it. To describe -this requires a little detail about how Mercurial adds changesets to a -repository and reads those changes. -</para> - -<para>When Mercurial <emphasis>writes</emphasis> metadata, it writes it straight into the -destination file. It writes file data first, then manifest data -(which contains pointers to the new file data), then changelog data -(which contains pointers to the new manifest data). Before the first -write to each file, it stores a record of where the end of the file -was in its transaction log. If the transaction must be rolled back, -Mercurial simply truncates each file back to the size it was before the -transaction began. -</para> - -<para>When Mercurial <emphasis>reads</emphasis> metadata, it reads the changelog first, -then everything else. Since a reader will only access parts of the -manifest or file metadata that it can see in the changelog, it can -never see partially written data. -</para> - -<para>Some controlling hooks (<literal role="hook">pretxncommit</literal> and -<literal role="hook">pretxnchangegroup</literal>) run when a transaction is almost complete. -All of the metadata has been written, but Mercurial can still roll the -transaction back and cause the newly-written data to disappear. -</para> - -<para>If one of these hooks runs for long, it opens a window of time during -which a reader can see the metadata for changesets that are not yet -permanent, and should not be thought of as <quote>really there</quote>. The -longer the hook runs, the longer that window is open. -</para> - -<sect2> -<title>The problem illustrated</title> - -<para>In principle, a good use for the <literal role="hook">pretxnchangegroup</literal> hook would -be to automatically build and test incoming changes before they are -accepted into a central repository. This could let you guarantee that -nobody can push changes to this repository that <quote>break the build</quote>. -But if a client can pull changes while they're being tested, the -usefulness of the test is zero; an unsuspecting someone can pull -untested changes, potentially breaking their build. -</para> - -<para>The safest technological answer to this challenge is to set up such a -<quote>gatekeeper</quote> repository as <emphasis>unidirectional</emphasis>. Let it take -changes pushed in from the outside, but do not allow anyone to pull -changes from it (use the <literal role="hook">preoutgoing</literal> hook to lock it down). -Configure a <literal role="hook">changegroup</literal> hook so that if a build or test -succeeds, the hook will push the new changes out to another repository -that people <emphasis>can</emphasis> pull from. -</para> - -<para>In practice, putting a centralised bottleneck like this in place is -not often a good idea, and transaction visibility has nothing to do -with the problem. As the size of a project&emdash;and the time it takes to -build and test&emdash;grows, you rapidly run into a wall with this <quote>try -before you buy</quote> approach, where you have more changesets to test than -time in which to deal with them. The inevitable result is frustration -on the part of all involved. -</para> - -<para>An approach that scales better is to get people to build and test -before they push, then run automated builds and tests centrally -<emphasis>after</emphasis> a push, to be sure all is well. The advantage of this -approach is that it does not impose a limit on the rate at which the -repository can accept changes. -</para> - -</sect2> -</sect1> -<sect1> -<title>A short tutorial on using hooks</title> -<para>\label{sec:hook:simple} -</para> - -<para>It is easy to write a Mercurial hook. Let's start with a hook that -runs when you finish a <command role="hg-cmd">hg commit</command>, and simply prints the hash of -the changeset you just created. The hook is called <literal role="hook">commit</literal>. -</para> - -<informalfigure> -<para> <!-- &interaction.hook.simple.init; --> - <caption><para>A simple hook that runs when a changeset is committed</para></caption> - \label{ex:hook:init} -</para> -</informalfigure> - -<para>All hooks follow the pattern in example <xref linkend="ex:hook:init"/>. You add -an entry to the <literal role="rc-hooks">hooks</literal> section of your <filename role="special"> /.hgrc</filename>. On the left -is the name of the event to trigger on; on the right is the action to -take. As you can see, you can run an arbitrary shell command in a -hook. Mercurial passes extra information to the hook using -environment variables (look for <envar>HG_NODE</envar> in the example). -</para> - -<sect2> -<title>Performing multiple actions per event</title> - -<para>Quite often, you will want to define more than one hook for a -particular kind of event, as shown in example <xref linkend="ex:hook:ext"/>. -Mercurial lets you do this by adding an <emphasis>extension</emphasis> to the end of -a hook's name. You extend a hook's name by giving the name of the -hook, followed by a full stop (the <quote><literal>.</literal></quote> character), followed -by some more text of your choosing. For example, Mercurial will run -both <literal>commit.foo</literal> and <literal>commit.bar</literal> when the -<literal>commit</literal> event occurs. -</para> - -<informalfigure> -<para> <!-- &interaction.hook.simple.ext; --> - <caption><para>Defining a second <literal role="hook">commit</para></caption> hook</literal> - \label{ex:hook:ext} -</para> -</informalfigure> - -<para>To give a well-defined order of execution when there are multiple -hooks defined for an event, Mercurial sorts hooks by extension, and -executes the hook commands in this sorted order. In the above -example, it will execute <literal>commit.bar</literal> before -<literal>commit.foo</literal>, and <literal>commit</literal> before both. -</para> - -<para>It is a good idea to use a somewhat descriptive extension when you -define a new hook. This will help you to remember what the hook was -for. If the hook fails, you'll get an error message that contains the -hook name and extension, so using a descriptive extension could give -you an immediate hint as to why the hook failed (see -section <xref linkend="sec:hook:perm"/> for an example). -</para> - -</sect2> -<sect2> -<title>Controlling whether an activity can proceed</title> -<para>\label{sec:hook:perm} -</para> - -<para>In our earlier examples, we used the <literal role="hook">commit</literal> hook, which is -run after a commit has completed. This is one of several Mercurial -hooks that run after an activity finishes. Such hooks have no way of -influencing the activity itself. -</para> - -<para>Mercurial defines a number of events that occur before an activity -starts; or after it starts, but before it finishes. Hooks that -trigger on these events have the added ability to choose whether the -activity can continue, or will abort. -</para> - -<para>The <literal role="hook">pretxncommit</literal> hook runs after a commit has all but -completed. In other words, the metadata representing the changeset -has been written out to disk, but the transaction has not yet been -allowed to complete. The <literal role="hook">pretxncommit</literal> hook has the ability to -decide whether the transaction can complete, or must be rolled back. -</para> - -<para>If the <literal role="hook">pretxncommit</literal> hook exits with a status code of zero, the -transaction is allowed to complete; the commit finishes; and the -<literal role="hook">commit</literal> hook is run. If the <literal role="hook">pretxncommit</literal> hook exits with -a non-zero status code, the transaction is rolled back; the metadata -representing the changeset is erased; and the <literal role="hook">commit</literal> hook is -not run. -</para> - -<informalfigure> -<para> <!-- &interaction.hook.simple.pretxncommit; --> - <caption><para>Using the <literal role="hook">pretxncommit</para></caption> hook to control commits</literal> - \label{ex:hook:pretxncommit} -</para> -</informalfigure> - -<para>The hook in example <xref linkend="ex:hook:pretxncommit"/> checks that a commit -comment contains a bug ID. If it does, the commit can complete. If -not, the commit is rolled back. -</para> - -</sect2> -</sect1> -<sect1> -<title>Writing your own hooks</title> - -<para>When you are writing a hook, you might find it useful to run Mercurial -either with the <option role="hg-opt-global">-v</option> option, or the <envar role="rc-item-ui">verbose</envar> config -item set to <quote>true</quote>. When you do so, Mercurial will print a message -before it calls each hook. -</para> - -<sect2> -<title>Choosing how your hook should run</title> -<para>\label{sec:hook:lang} -</para> - -<para>You can write a hook either as a normal program&emdash;typically a shell -script&emdash;or as a Python function that is executed within the Mercurial -process. -</para> - -<para>Writing a hook as an external program has the advantage that it -requires no knowledge of Mercurial's internals. You can call normal -Mercurial commands to get any added information you need. The -trade-off is that external hooks are slower than in-process hooks. -</para> - -<para>An in-process Python hook has complete access to the Mercurial API, -and does not <quote>shell out</quote> to another process, so it is inherently -faster than an external hook. It is also easier to obtain much of the -information that a hook requires by using the Mercurial API than by -running Mercurial commands. -</para> - -<para>If you are comfortable with Python, or require high performance, -writing your hooks in Python may be a good choice. However, when you -have a straightforward hook to write and you don't need to care about -performance (probably the majority of hooks), a shell script is -perfectly fine. -</para> - -</sect2> -<sect2> -<title>Hook parameters</title> -<para>\label{sec:hook:param} -</para> - -<para>Mercurial calls each hook with a set of well-defined parameters. In -Python, a parameter is passed as a keyword argument to your hook -function. For an external program, a parameter is passed as an -environment variable. -</para> - -<para>Whether your hook is written in Python or as a shell script, the -hook-specific parameter names and values will be the same. A boolean -parameter will be represented as a boolean value in Python, but as the -number 1 (for <quote>true</quote>) or 0 (for <quote>false</quote>) as an environment -variable for an external hook. If a hook parameter is named -<literal>foo</literal>, the keyword argument for a Python hook will also be -named <literal>foo</literal>, while the environment variable for an external -hook will be named <literal>HG_FOO</literal>. -</para> - -</sect2> -<sect2> -<title>Hook return values and activity control</title> - -<para>A hook that executes successfully must exit with a status of zero if -external, or return boolean <quote>false</quote> if in-process. Failure is -indicated with a non-zero exit status from an external hook, or an -in-process hook returning boolean <quote>true</quote>. If an in-process hook -raises an exception, the hook is considered to have failed. -</para> - -<para>For a hook that controls whether an activity can proceed, zero/false -means <quote>allow</quote>, while non-zero/true/exception means <quote>deny</quote>. -</para> - -</sect2> -<sect2> -<title>Writing an external hook</title> - -<para>When you define an external hook in your <filename role="special"> /.hgrc</filename>\ and the hook is run, -its value is passed to your shell, which interprets it. This means -that you can use normal shell constructs in the body of the hook. -</para> - -<para>An executable hook is always run with its current directory set to a -repository's root directory. -</para> - -<para>Each hook parameter is passed in as an environment variable; the name -is upper-cased, and prefixed with the string <quote><literal>HG_</literal></quote>. -</para> - -<para>With the exception of hook parameters, Mercurial does not set or -modify any environment variables when running a hook. This is useful -to remember if you are writing a site-wide hook that may be run by a -number of different users with differing environment variables set. -In multi-user situations, you should not rely on environment variables -being set to the values you have in your environment when testing the -hook. -</para> - -</sect2> -<sect2> -<title>Telling Mercurial to use an in-process hook</title> - -<para>The <filename role="special"> /.hgrc</filename>\ syntax for defining an in-process hook is slightly -different than for an executable hook. The value of the hook must -start with the text <quote><literal>python:</literal></quote>, and continue with the -fully-qualified name of a callable object to use as the hook's value. -</para> - -<para>The module in which a hook lives is automatically imported when a hook -is run. So long as you have the module name and <envar>PYTHONPATH</envar> -right, it should <quote>just work</quote>. -</para> - -<para>The following <filename role="special"> /.hgrc</filename>\ example snippet illustrates the syntax and -meaning of the notions we just described. -</para> -<programlisting> -<para> [hooks] - commit.example = python:mymodule.submodule.myhook -</para> -</programlisting> -<para>When Mercurial runs the <literal>commit.example</literal> hook, it imports -<literal>mymodule.submodule</literal>, looks for the callable object named -<literal>myhook</literal>, and calls it. -</para> - -</sect2> -<sect2> -<title>Writing an in-process hook</title> - -<para>The simplest in-process hook does nothing, but illustrates the basic -shape of the hook API: -</para> -<programlisting> -<para> def myhook(ui, repo, **kwargs): - pass -</para> -</programlisting> -<para>The first argument to a Python hook is always a -<literal role="py-mod-mercurial.ui">ui</literal> object. The second is a repository object; -at the moment, it is always an instance of -<literal role="py-mod-mercurial.localrepo">localrepository</literal>. Following these two -arguments are other keyword arguments. Which ones are passed in -depends on the hook being called, but a hook can ignore arguments it -doesn't care about by dropping them into a keyword argument dict, as -with <literal>**kwargs</literal> above. -</para> - -</sect2> -</sect1> -<sect1> -<title>Some hook examples</title> - -<sect2> -<title>Writing meaningful commit messages</title> - -<para>It's hard to imagine a useful commit message being very short. The -simple <literal role="hook">pretxncommit</literal> hook of figure <xref linkend="ex:hook:msglen.go"/> -will prevent you from committing a changeset with a message that is -less than ten bytes long. -</para> - -<informalfigure> -<para> <!-- &interaction.hook.msglen.go; --> - <caption><para>A hook that forbids overly short commit messages</para></caption> - \label{ex:hook:msglen.go} -</para> -</informalfigure> - -</sect2> -<sect2> -<title>Checking for trailing whitespace</title> - -<para>An interesting use of a commit-related hook is to help you to write -cleaner code. A simple example of <quote>cleaner code</quote> is the dictum that -a change should not add any new lines of text that contain <quote>trailing -whitespace</quote>. Trailing whitespace is a series of space and tab -characters at the end of a line of text. In most cases, trailing -whitespace is unnecessary, invisible noise, but it is occasionally -problematic, and people often prefer to get rid of it. -</para> - -<para>You can use either the <literal role="hook">precommit</literal> or <literal role="hook">pretxncommit</literal> hook to -tell whether you have a trailing whitespace problem. If you use the -<literal role="hook">precommit</literal> hook, the hook will not know which files you are -committing, so it will have to check every modified file in the -repository for trailing white space. If you want to commit a change -to just the file <filename>foo</filename>, but the file <filename>bar</filename> contains -trailing whitespace, doing a check in the <literal role="hook">precommit</literal> hook will -prevent you from committing <filename>foo</filename> due to the problem with -<filename>bar</filename>. This doesn't seem right. -</para> - -<para>Should you choose the <literal role="hook">pretxncommit</literal> hook, the check won't occur -until just before the transaction for the commit completes. This will -allow you to check for problems only the exact files that are being -committed. However, if you entered the commit message interactively -and the hook fails, the transaction will roll back; you'll have to -re-enter the commit message after you fix the trailing whitespace and -run <command role="hg-cmd">hg commit</command> again. -</para> - -<informalfigure> -<para> <!-- &interaction.hook.ws.simple; --> - <caption><para>A simple hook that checks for trailing whitespace</para></caption> - \label{ex:hook:ws.simple} -</para> -</informalfigure> - -<para>Figure <xref linkend="ex:hook:ws.simple"/> introduces a simple <literal role="hook">pretxncommit</literal> -hook that checks for trailing whitespace. This hook is short, but not -very helpful. It exits with an error status if a change adds a line -with trailing whitespace to any file, but does not print any -information that might help us to identify the offending file or -line. It also has the nice property of not paying attention to -unmodified lines; only lines that introduce new trailing whitespace -cause problems. -</para> - -<informalfigure> -<para> <!-- &interaction.hook.ws.better; --> - <caption><para>A better trailing whitespace hook</para></caption> - \label{ex:hook:ws.better} -</para> -</informalfigure> - -<para>The example of figure <xref linkend="ex:hook:ws.better"/> is much more complex, -but also more useful. It parses a unified diff to see if any lines -add trailing whitespace, and prints the name of the file and the line -number of each such occurrence. Even better, if the change adds -trailing whitespace, this hook saves the commit comment and prints the -name of the save file before exiting and telling Mercurial to roll the -transaction back, so you can use -<command role="hg-cmd">hg commit <option role="hg-opt-commit">-l</option> <emphasis>filename</emphasis></command> to reuse the -saved commit message once you've corrected the problem. -</para> - -<para>As a final aside, note in figure <xref linkend="ex:hook:ws.better"/> the use of -<command>perl</command>'s in-place editing feature to get rid of trailing -whitespace from a file. This is concise and useful enough that I will -reproduce it here. -</para> -<programlisting> -<para> perl -pi -e 's,\textbackslash{}s+$,,' filename -</para> -</programlisting> - -</sect2> -</sect1> -<sect1> -<title>Bundled hooks</title> - -<para>Mercurial ships with several bundled hooks. You can find them in the -<filename class="directory">hgext</filename> directory of a Mercurial source tree. If you are -using a Mercurial binary package, the hooks will be located in the -<filename class="directory">hgext</filename> directory of wherever your package installer put -Mercurial. -</para> - -<sect2> -<title><literal role="hg-ext">acl</literal>&emdash;access control for parts of a repository</title> - -<para>The <literal role="hg-ext">acl</literal> extension lets you control which remote users are -allowed to push changesets to a networked server. You can protect any -portion of a repository (including the entire repo), so that a -specific remote user can push changes that do not affect the protected -portion. -</para> - -<para>This extension implements access control based on the identity of the -user performing a push, <emphasis>not</emphasis> on who committed the changesets -they're pushing. It makes sense to use this hook only if you have a -locked-down server environment that authenticates remote users, and -you want to be sure that only specific users are allowed to push -changes to that server. -</para> - -<sect3> -<title>Configuring the <literal role="hook">acl</literal> hook</title> - -<para>In order to manage incoming changesets, the <literal role="hg-ext">acl</literal> hook must be -used as a <literal role="hook">pretxnchangegroup</literal> hook. This lets it see which files -are modified by each incoming changeset, and roll back a group of -changesets if they modify <quote>forbidden</quote> files. Example: -</para> -<programlisting> -<para> [hooks] - pretxnchangegroup.acl = python:hgext.acl.hook -</para> -</programlisting> - -<para>The <literal role="hg-ext">acl</literal> extension is configured using three sections. -</para> - -<para>The <literal role="rc-acl">acl</literal> section has only one entry, <envar role="rc-item-acl">sources</envar>, -which lists the sources of incoming changesets that the hook should -pay attention to. You don't normally need to configure this section. -</para> -<itemizedlist> -<listitem><para><envar role="rc-item-acl">serve</envar>: Control incoming changesets that are arriving - from a remote repository over http or ssh. This is the default - value of <envar role="rc-item-acl">sources</envar>, and usually the only setting you'll - need for this configuration item. -</para> -</listitem> -<listitem><para><envar role="rc-item-acl">pull</envar>: Control incoming changesets that are - arriving via a pull from a local repository. -</para> -</listitem> -<listitem><para><envar role="rc-item-acl">push</envar>: Control incoming changesets that are - arriving via a push from a local repository. -</para> -</listitem> -<listitem><para><envar role="rc-item-acl">bundle</envar>: Control incoming changesets that are - arriving from another repository via a bundle. -</para> -</listitem></itemizedlist> - -<para>The <literal role="rc-acl.allow">acl.allow</literal> section controls the users that are allowed to -add changesets to the repository. If this section is not present, all -users that are not explicitly denied are allowed. If this section is -present, all users that are not explicitly allowed are denied (so an -empty section means that all users are denied). -</para> - -<para>The <literal role="rc-acl.deny">acl.deny</literal> section determines which users are denied -from adding changesets to the repository. If this section is not -present or is empty, no users are denied. -</para> - -<para>The syntaxes for the <literal role="rc-acl.allow">acl.allow</literal> and <literal role="rc-acl.deny">acl.deny</literal> -sections are identical. On the left of each entry is a glob pattern -that matches files or directories, relative to the root of the -repository; on the right, a user name. -</para> - -<para>In the following example, the user <literal>docwriter</literal> can only push -changes to the <filename class="directory">docs</filename> subtree of the repository, while -<literal>intern</literal> can push changes to any file or directory except -<filename class="directory">source/sensitive</filename>. -</para> -<programlisting> -<para> [acl.allow] - docs/** = docwriter -</para> - -<para> [acl.deny] - source/sensitive/** = intern -</para> -</programlisting> - -</sect3> -<sect3> -<title>Testing and troubleshooting</title> - -<para>If you want to test the <literal role="hg-ext">acl</literal> hook, run it with Mercurial's -debugging output enabled. Since you'll probably be running it on a -server where it's not convenient (or sometimes possible) to pass in -the <option role="hg-opt-global">--debug</option> option, don't forget that you can enable -debugging output in your <filename role="special"> /.hgrc</filename>: -</para> -<programlisting> -<para> [ui] - debug = true -</para> -</programlisting> -<para>With this enabled, the <literal role="hg-ext">acl</literal> hook will print enough information -to let you figure out why it is allowing or forbidding pushes from -specific users. -</para> - -</sect3> -</sect2> -<sect2> -<title><literal role="hg-ext">bugzilla</literal>&emdash;integration with Bugzilla</title> - -<para>The <literal role="hg-ext">bugzilla</literal> extension adds a comment to a Bugzilla bug -whenever it finds a reference to that bug ID in a commit comment. You -can install this hook on a shared server, so that any time a remote -user pushes changes to this server, the hook gets run. -</para> - -<para>It adds a comment to the bug that looks like this (you can configure -the contents of the comment&emdash;see below): -</para> -<programlisting> -<para> Changeset aad8b264143a, made by Joe User <joe.user@domain.com> in - the frobnitz repository, refers to this bug. -</para> - -<para> For complete details, see - http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a -</para> - -<para> Changeset description: - Fix bug 10483 by guarding against some NULL pointers -</para> -</programlisting> -<para>The value of this hook is that it automates the process of updating a -bug any time a changeset refers to it. If you configure the hook -properly, it makes it easy for people to browse straight from a -Bugzilla bug to a changeset that refers to that bug. -</para> - -<para>You can use the code in this hook as a starting point for some more -exotic Bugzilla integration recipes. Here are a few possibilities: -</para> -<itemizedlist> -<listitem><para>Require that every changeset pushed to the server have a valid - bug ID in its commit comment. In this case, you'd want to configure - the hook as a <literal role="hook">pretxncommit</literal> hook. This would allow the hook - to reject changes that didn't contain bug IDs. -</para> -</listitem> -<listitem><para>Allow incoming changesets to automatically modify the - <emphasis>state</emphasis> of a bug, as well as simply adding a comment. For - example, the hook could recognise the string <quote>fixed bug 31337</quote> as - indicating that it should update the state of bug 31337 to - <quote>requires testing</quote>. -</para> -</listitem></itemizedlist> - -<sect3> -<title>Configuring the <literal role="hook">bugzilla</literal> hook</title> -<para>\label{sec:hook:bugzilla:config} -</para> - -<para>You should configure this hook in your server's <filename role="special"> /.hgrc</filename>\ as an -<literal role="hook">incoming</literal> hook, for example as follows: -</para> -<programlisting> -<para> [hooks] - incoming.bugzilla = python:hgext.bugzilla.hook -</para> -</programlisting> - -<para>Because of the specialised nature of this hook, and because Bugzilla -was not written with this kind of integration in mind, configuring -this hook is a somewhat involved process. -</para> - -<para>Before you begin, you must install the MySQL bindings for Python on -the host(s) where you'll be running the hook. If this is not -available as a binary package for your system, you can download it -from <citation>web:mysql-python</citation>. -</para> - -<para>Configuration information for this hook lives in the -<literal role="rc-bugzilla">bugzilla</literal> section of your <filename role="special"> /.hgrc</filename>. -</para> -<itemizedlist> -<listitem><para><envar role="rc-item-bugzilla">version</envar>: The version of Bugzilla installed on - the server. The database schema that Bugzilla uses changes - occasionally, so this hook has to know exactly which schema to use. - At the moment, the only version supported is <literal>2.16</literal>. -</para> -</listitem> -<listitem><para><envar role="rc-item-bugzilla">host</envar>: The hostname of the MySQL server that - stores your Bugzilla data. The database must be configured to allow - connections from whatever host you are running the <literal role="hook">bugzilla</literal> - hook on. -</para> -</listitem> -<listitem><para><envar role="rc-item-bugzilla">user</envar>: The username with which to connect to - the MySQL server. The database must be configured to allow this - user to connect from whatever host you are running the - <literal role="hook">bugzilla</literal> hook on. This user must be able to access and - modify Bugzilla tables. The default value of this item is - <literal>bugs</literal>, which is the standard name of the Bugzilla user in a - MySQL database. -</para> -</listitem> -<listitem><para><envar role="rc-item-bugzilla">password</envar>: The MySQL password for the user you - configured above. This is stored as plain text, so you should make - sure that unauthorised users cannot read the <filename role="special"> /.hgrc</filename>\ file where you - store this information. -</para> -</listitem> -<listitem><para><envar role="rc-item-bugzilla">db</envar>: The name of the Bugzilla database on the - MySQL server. The default value of this item is <literal>bugs</literal>, - which is the standard name of the MySQL database where Bugzilla - stores its data. -</para> -</listitem> -<listitem><para><envar role="rc-item-bugzilla">notify</envar>: If you want Bugzilla to send out a - notification email to subscribers after this hook has added a - comment to a bug, you will need this hook to run a command whenever - it updates the database. The command to run depends on where you - have installed Bugzilla, but it will typically look something like - this, if you have Bugzilla installed in - <filename class="directory">/var/www/html/bugzilla</filename>: -</para> -</listitem><programlisting> -<listitem><para> cd /var/www/html/bugzilla && ./processmail %s nobody@nowhere.com -</para> -</listitem></programlisting> -<listitem><para> The Bugzilla <literal>processmail</literal> program expects to be given a - bug ID (the hook replaces <quote><literal>%s</literal></quote> with the bug ID) and an - email address. It also expects to be able to write to some files in - the directory that it runs in. If Bugzilla and this hook are not - installed on the same machine, you will need to find a way to run - <literal>processmail</literal> on the server where Bugzilla is installed. -</para> -</listitem></itemizedlist> - -</sect3> -<sect3> -<title>Mapping committer names to Bugzilla user names</title> - -<para>By default, the <literal role="hg-ext">bugzilla</literal> hook tries to use the email address -of a changeset's committer as the Bugzilla user name with which to -update a bug. If this does not suit your needs, you can map committer -email addresses to Bugzilla user names using a <literal role="rc-usermap">usermap</literal> -section. -</para> - -<para>Each item in the <literal role="rc-usermap">usermap</literal> section contains an email address -on the left, and a Bugzilla user name on the right. -</para> -<programlisting> -<para> [usermap] - jane.user@example.com = jane -</para> -</programlisting> -<para>You can either keep the <literal role="rc-usermap">usermap</literal> data in a normal <filename role="special"> /.hgrc</filename>, or -tell the <literal role="hg-ext">bugzilla</literal> hook to read the information from an -external <filename>usermap</filename> file. In the latter case, you can store -<filename>usermap</filename> data by itself in (for example) a user-modifiable -repository. This makes it possible to let your users maintain their -own <envar role="rc-item-bugzilla">usermap</envar> entries. The main <filename role="special"> /.hgrc</filename>\ file might -look like this: -</para> -<programlisting> -<para> # regular hgrc file refers to external usermap file - [bugzilla] - usermap = /home/hg/repos/userdata/bugzilla-usermap.conf -</para> -</programlisting> -<para>While the <filename>usermap</filename> file that it refers to might look like -this: -</para> -<programlisting> -<para> # bugzilla-usermap.conf - inside a hg repository - [usermap] - stephanie@example.com = steph -</para> -</programlisting> - -</sect3> -<sect3> -<title>Configuring the text that gets added to a bug</title> - -<para>You can configure the text that this hook adds as a comment; you -specify it in the form of a Mercurial template. Several <filename role="special"> /.hgrc</filename>\ -entries (still in the <literal role="rc-bugzilla">bugzilla</literal> section) control this -behaviour. -</para> -<itemizedlist> -<listitem><para><literal>strip</literal>: The number of leading path elements to strip - from a repository's path name to construct a partial path for a URL. - For example, if the repositories on your server live under - <filename class="directory">/home/hg/repos</filename>, and you have a repository whose path is - <filename class="directory">/home/hg/repos/app/tests</filename>, then setting <literal>strip</literal> to - <literal>4</literal> will give a partial path of <filename class="directory">app/tests</filename>. The - hook will make this partial path available when expanding a - template, as <literal>webroot</literal>. -</para> -</listitem> -<listitem><para><literal>template</literal>: The text of the template to use. In addition - to the usual changeset-related variables, this template can use - <literal>hgweb</literal> (the value of the <literal>hgweb</literal> configuration item - above) and <literal>webroot</literal> (the path constructed using - <literal>strip</literal> above). -</para> -</listitem></itemizedlist> - -<para>In addition, you can add a <envar role="rc-item-web">baseurl</envar> item to the -<literal role="rc-web">web</literal> section of your <filename role="special"> /.hgrc</filename>. The <literal role="hg-ext">bugzilla</literal> hook will -make this available when expanding a template, as the base string to -use when constructing a URL that will let users browse from a Bugzilla -comment to view a changeset. Example: -</para> -<programlisting> -<para> [web] - baseurl = http://hg.domain.com/ -</para> -</programlisting> - -<para>Here is an example set of <literal role="hg-ext">bugzilla</literal> hook config information. -</para> -<programlisting> -<para> [bugzilla] - host = bugzilla.example.com - password = mypassword - version = 2.16 - # server-side repos live in /home/hg/repos, so strip 4 leading - # separators - strip = 4 - hgweb = http://hg.example.com/ - usermap = /home/hg/repos/notify/bugzilla.conf - template = Changeset {node|short}, made by {author} in the {webroot} - repo, refers to this bug.\\nFor complete details, see - {hgweb}{webroot}?cmd=changeset;node={node|short}\\nChangeset - description:\\n\\t{desc|tabindent} -</para> -</programlisting> - -</sect3> -<sect3> -<title>Testing and troubleshooting</title> - -<para>The most common problems with configuring the <literal role="hg-ext">bugzilla</literal> hook -relate to running Bugzilla's <filename>processmail</filename> script and mapping -committer names to user names. -</para> - -<para>Recall from section <xref linkend="sec:hook:bugzilla:config"/> above that the user -that runs the Mercurial process on the server is also the one that -will run the <filename>processmail</filename> script. The -<filename>processmail</filename> script sometimes causes Bugzilla to write to -files in its configuration directory, and Bugzilla's configuration -files are usually owned by the user that your web server runs under. -</para> - -<para>You can cause <filename>processmail</filename> to be run with the suitable -user's identity using the <command>sudo</command> command. Here is an example -entry for a <filename>sudoers</filename> file. -</para> -<programlisting> -<para> hg_user = (httpd_user) NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s -</para> -</programlisting> -<para>This allows the <literal>hg_user</literal> user to run a -<filename>processmail-wrapper</filename> program under the identity of -<literal>httpd_user</literal>. -</para> - -<para>This indirection through a wrapper script is necessary, because -<filename>processmail</filename> expects to be run with its current directory -set to wherever you installed Bugzilla; you can't specify that kind of -constraint in a <filename>sudoers</filename> file. The contents of the wrapper -script are simple: -</para> -<programlisting> -<para> #!/bin/sh - cd `dirname $0` && ./processmail "$1" nobody@example.com -</para> -</programlisting> -<para>It doesn't seem to matter what email address you pass to -<filename>processmail</filename>. -</para> - -<para>If your <literal role="rc-usermap">usermap</literal> is not set up correctly, users will see an -error message from the <literal role="hg-ext">bugzilla</literal> hook when they push changes -to the server. The error message will look like this: -</para> -<programlisting> -<para> cannot find bugzilla user id for john.q.public@example.com -</para> -</programlisting> -<para>What this means is that the committer's address, -<literal>john.q.public@example.com</literal>, is not a valid Bugzilla user name, -nor does it have an entry in your <literal role="rc-usermap">usermap</literal> that maps it to -a valid Bugzilla user name. -</para> - -</sect3> -</sect2> -<sect2> -<title><literal role="hg-ext">notify</literal>&emdash;send email notifications</title> - -<para>Although Mercurial's built-in web server provides RSS feeds of changes -in every repository, many people prefer to receive change -notifications via email. The <literal role="hg-ext">notify</literal> hook lets you send out -notifications to a set of email addresses whenever changesets arrive -that those subscribers are interested in. -</para> - -<para>As with the <literal role="hg-ext">bugzilla</literal> hook, the <literal role="hg-ext">notify</literal> hook is -template-driven, so you can customise the contents of the notification -messages that it sends. -</para> - -<para>By default, the <literal role="hg-ext">notify</literal> hook includes a diff of every changeset -that it sends out; you can limit the size of the diff, or turn this -feature off entirely. It is useful for letting subscribers review -changes immediately, rather than clicking to follow a URL. -</para> - -<sect3> -<title>Configuring the <literal role="hg-ext">notify</literal> hook</title> - -<para>You can set up the <literal role="hg-ext">notify</literal> hook to send one email message per -incoming changeset, or one per incoming group of changesets (all those -that arrived in a single pull or push). -</para> -<programlisting> -<para> [hooks] - # send one email per group of changes - changegroup.notify = python:hgext.notify.hook - # send one email per change - incoming.notify = python:hgext.notify.hook -</para> -</programlisting> - -<para>Configuration information for this hook lives in the -<literal role="rc-notify">notify</literal> section of a <filename role="special"> /.hgrc</filename>\ file. -</para> -<itemizedlist> -<listitem><para><envar role="rc-item-notify">test</envar>: By default, this hook does not send out - email at all; instead, it prints the message that it <emphasis>would</emphasis> - send. Set this item to <literal>false</literal> to allow email to be sent. - The reason that sending of email is turned off by default is that it - takes several tries to configure this extension exactly as you would - like, and it would be bad form to spam subscribers with a number of - <quote>broken</quote> notifications while you debug your configuration. -</para> -</listitem> -<listitem><para><envar role="rc-item-notify">config</envar>: The path to a configuration file that - contains subscription information. This is kept separate from the - main <filename role="special"> /.hgrc</filename>\ so that you can maintain it in a repository of its own. - People can then clone that repository, update their subscriptions, - and push the changes back to your server. -</para> -</listitem> -<listitem><para><envar role="rc-item-notify">strip</envar>: The number of leading path separator - characters to strip from a repository's path, when deciding whether - a repository has subscribers. For example, if the repositories on - your server live in <filename class="directory">/home/hg/repos</filename>, and <literal role="hg-ext">notify</literal> is - considering a repository named <filename class="directory">/home/hg/repos/shared/test</filename>, - setting <envar role="rc-item-notify">strip</envar> to <literal>4</literal> will cause - <literal role="hg-ext">notify</literal> to trim the path it considers down to - <filename class="directory">shared/test</filename>, and it will match subscribers against that. -</para> -</listitem> -<listitem><para><envar role="rc-item-notify">template</envar>: The template text to use when sending - messages. This specifies both the contents of the message header - and its body. -</para> -</listitem> -<listitem><para><envar role="rc-item-notify">maxdiff</envar>: The maximum number of lines of diff - data to append to the end of a message. If a diff is longer than - this, it is truncated. By default, this is set to 300. Set this to - <literal>0</literal> to omit diffs from notification emails. -</para> -</listitem> -<listitem><para><envar role="rc-item-notify">sources</envar>: A list of sources of changesets to - consider. This lets you limit <literal role="hg-ext">notify</literal> to only sending out - email about changes that remote users pushed into this repository - via a server, for example. See section <xref linkend="sec:hook:sources"/> for - the sources you can specify here. -</para> -</listitem></itemizedlist> - -<para>If you set the <envar role="rc-item-web">baseurl</envar> item in the <literal role="rc-web">web</literal> -section, you can use it in a template; it will be available as -<literal>webroot</literal>. -</para> - -<para>Here is an example set of <literal role="hg-ext">notify</literal> configuration information. -</para> -<programlisting> -<para> [notify] - # really send email - test = false - # subscriber data lives in the notify repo - config = /home/hg/repos/notify/notify.conf - # repos live in /home/hg/repos on server, so strip 4 "/" chars - strip = 4 - template = X-Hg-Repo: {webroot} - Subject: {webroot}: {desc|firstline|strip} - From: {author} -</para> - -<para> changeset {node|short} in {root} - details: {baseurl}{webroot}?cmd=changeset;node={node|short} - description: - {desc|tabindent|strip} -</para> - -<para> [web] - baseurl = http://hg.example.com/ -</para> -</programlisting> - -<para>This will produce a message that looks like the following: -</para> -<programlisting> -<para> X-Hg-Repo: tests/slave - Subject: tests/slave: Handle error case when slave has no buffers - Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT) -</para> - -<para> changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave - details: http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b5 - description: - Handle error case when slave has no buffers - diffs (54 lines): -</para> - -<para> diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h - &emdash; a/include/tests.h Wed Aug 02 15:19:52 2006 -0700 - +++ b/include/tests.h Wed Aug 02 15:25:26 2006 -0700 - @@ -212,6 +212,15 @@ static __inline__ void test_headers(void *h) - [...snip...] -</para> -</programlisting> - -</sect3> -<sect3> -<title>Testing and troubleshooting</title> - -<para>Do not forget that by default, the <literal role="hg-ext">notify</literal> extension \emph{will - not send any mail} until you explicitly configure it to do so, by -setting <envar role="rc-item-notify">test</envar> to <literal>false</literal>. Until you do that, -it simply prints the message it <emphasis>would</emphasis> send. -</para> - -</sect3> -</sect2> -</sect1> -<sect1> -<title>Information for writers of hooks</title> -<para>\label{sec:hook:ref} -</para> - -<sect2> -<title>In-process hook execution</title> - -<para>An in-process hook is called with arguments of the following form: -</para> -<programlisting> -<para> def myhook(ui, repo, **kwargs): - pass -</para> -</programlisting> -<para>The <literal>ui</literal> parameter is a <literal role="py-mod-mercurial.ui">ui</literal> object. -The <literal>repo</literal> parameter is a -<literal role="py-mod-mercurial.localrepo">localrepository</literal> object. The -names and values of the <literal>**kwargs</literal> parameters depend on the -hook being invoked, with the following common features: -</para> -<itemizedlist> -<listitem><para>If a parameter is named <literal>node</literal> or - <literal>parent<emphasis>N</emphasis></literal>, it will contain a hexadecimal changeset ID. - The empty string is used to represent <quote>null changeset ID</quote> instead - of a string of zeroes. -</para> -</listitem> -<listitem><para>If a parameter is named <literal>url</literal>, it will contain the URL of - a remote repository, if that can be determined. -</para> -</listitem> -<listitem><para>Boolean-valued parameters are represented as Python - <literal>bool</literal> objects. -</para> -</listitem></itemizedlist> - -<para>An in-process hook is called without a change to the process's working -directory (unlike external hooks, which are run in the root of the -repository). It must not change the process's working directory, or -it will cause any calls it makes into the Mercurial API to fail. -</para> - -<para>If a hook returns a boolean <quote>false</quote> value, it is considered to have -succeeded. If it returns a boolean <quote>true</quote> value or raises an -exception, it is considered to have failed. A useful way to think of -the calling convention is <quote>tell me if you fail</quote>. -</para> - -<para>Note that changeset IDs are passed into Python hooks as hexadecimal -strings, not the binary hashes that Mercurial's APIs normally use. To -convert a hash from hex to binary, use the -\pymodfunc{mercurial.node}{bin} function. -</para> - -</sect2> -<sect2> -<title>External hook execution</title> - -<para>An external hook is passed to the shell of the user running Mercurial. -Features of that shell, such as variable substitution and command -redirection, are available. The hook is run in the root directory of -the repository (unlike in-process hooks, which are run in the same -directory that Mercurial was run in). -</para> - -<para>Hook parameters are passed to the hook as environment variables. Each -environment variable's name is converted in upper case and prefixed -with the string <quote><literal>HG_</literal></quote>. For example, if the name of a -parameter is <quote><literal>node</literal></quote>, the name of the environment variable -representing that parameter will be <quote><literal>HG_NODE</literal></quote>. -</para> - -<para>A boolean parameter is represented as the string <quote><literal>1</literal></quote> for -<quote>true</quote>, <quote><literal>0</literal></quote> for <quote>false</quote>. If an environment variable is -named <envar>HG_NODE</envar>, <envar>HG_PARENT1</envar> or <envar>HG_PARENT2</envar>, it -contains a changeset ID represented as a hexadecimal string. The -empty string is used to represent <quote>null changeset ID</quote> instead of a -string of zeroes. If an environment variable is named -<envar>HG_URL</envar>, it will contain the URL of a remote repository, if -that can be determined. -</para> - -<para>If a hook exits with a status of zero, it is considered to have -succeeded. If it exits with a non-zero status, it is considered to -have failed. -</para> - -</sect2> -<sect2> -<title>Finding out where changesets come from</title> - -<para>A hook that involves the transfer of changesets between a local -repository and another may be able to find out information about the -<quote>far side</quote>. Mercurial knows <emphasis>how</emphasis> changes are being -transferred, and in many cases <emphasis>where</emphasis> they are being transferred -to or from. -</para> - -<sect3> -<title>Sources of changesets</title> -<para>\label{sec:hook:sources} -</para> - -<para>Mercurial will tell a hook what means are, or were, used to transfer -changesets between repositories. This is provided by Mercurial in a -Python parameter named <literal>source</literal>, or an environment variable named -<envar>HG_SOURCE</envar>. -</para> - -<itemizedlist> -<listitem><para><literal>serve</literal>: Changesets are transferred to or from a remote - repository over http or ssh. -</para> -</listitem> -<listitem><para><literal>pull</literal>: Changesets are being transferred via a pull from - one repository into another. -</para> -</listitem> -<listitem><para><literal>push</literal>: Changesets are being transferred via a push from - one repository into another. -</para> -</listitem> -<listitem><para><literal>bundle</literal>: Changesets are being transferred to or from a - bundle. -</para> -</listitem></itemizedlist> - -</sect3> -<sect3> -<title>Where changes are going&emdash;remote repository URLs</title> -<para>\label{sec:hook:url} -</para> - -<para>When possible, Mercurial will tell a hook the location of the <quote>far -side</quote> of an activity that transfers changeset data between -repositories. This is provided by Mercurial in a Python parameter -named <literal>url</literal>, or an environment variable named <envar>HG_URL</envar>. -</para> - -<para>This information is not always known. If a hook is invoked in a -repository that is being served via http or ssh, Mercurial cannot tell -where the remote repository is, but it may know where the client is -connecting from. In such cases, the URL will take one of the -following forms: -</para> -<itemizedlist> -<listitem><para><literal>remote:ssh:<emphasis>ip-address</emphasis></literal>&emdash;remote ssh client, at - the given IP address. -</para> -</listitem> -<listitem><para><literal>remote:http:<emphasis>ip-address</emphasis></literal>&emdash;remote http client, at - the given IP address. If the client is using SSL, this will be of - the form <literal>remote:https:<emphasis>ip-address</emphasis></literal>. -</para> -</listitem> -<listitem><para>Empty&emdash;no information could be discovered about the remote - client. -</para> -</listitem></itemizedlist> - -</sect3> -</sect2> -</sect1> -<sect1> -<title>Hook reference</title> - -<sect2> -<title><literal role="hook">changegroup</literal>&emdash;after remote changesets added</title> -<para>\label{sec:hook:changegroup} -</para> - -<para>This hook is run after a group of pre-existing changesets has been -added to the repository, for example via a <command role="hg-cmd">hg pull</command> or -<command role="hg-cmd">hg unbundle</command>. This hook is run once per operation that added one -or more changesets. This is in contrast to the <literal role="hook">incoming</literal> hook, -which is run once per changeset, regardless of whether the changesets -arrive in a group. -</para> - -<para>Some possible uses for this hook include kicking off an automated -build or test of the added changesets, updating a bug database, or -notifying subscribers that a repository contains new changes. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>node</literal>: A changeset ID. The changeset ID of the first - changeset in the group that was added. All changesets between this - and \index{tags!<literal>tip</literal>}<literal>tip</literal>, inclusive, were added by - a single <command role="hg-cmd">hg pull</command>, <command role="hg-cmd">hg push</command> or <command role="hg-cmd">hg unbundle</command>. -</para> -</listitem> -<listitem><para><literal>source</literal>: A string. The source of these changes. See - section <xref linkend="sec:hook:sources"/> for details. -</para> -</listitem> -<listitem><para><literal>url</literal>: A URL. The location of the remote repository, if - known. See section <xref linkend="sec:hook:url"/> for more information. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">incoming</literal> (section <xref linkend="sec:hook:incoming"/>), -<literal role="hook">prechangegroup</literal> (section <xref linkend="sec:hook:prechangegroup"/>), -<literal role="hook">pretxnchangegroup</literal> (section <xref linkend="sec:hook:pretxnchangegroup"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">commit</literal>&emdash;after a new changeset is created</title> -<para>\label{sec:hook:commit} -</para> - -<para>This hook is run after a new changeset has been created. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>node</literal>: A changeset ID. The changeset ID of the newly - committed changeset. -</para> -</listitem> -<listitem><para><literal>parent1</literal>: A changeset ID. The changeset ID of the first - parent of the newly committed changeset. -</para> -</listitem> -<listitem><para><literal>parent2</literal>: A changeset ID. The changeset ID of the second - parent of the newly committed changeset. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">precommit</literal> (section <xref linkend="sec:hook:precommit"/>), -<literal role="hook">pretxncommit</literal> (section <xref linkend="sec:hook:pretxncommit"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">incoming</literal>&emdash;after one remote changeset is added</title> -<para>\label{sec:hook:incoming} -</para> - -<para>This hook is run after a pre-existing changeset has been added to the -repository, for example via a <command role="hg-cmd">hg push</command>. If a group of changesets -was added in a single operation, this hook is called once for each -added changeset. -</para> - -<para>You can use this hook for the same purposes as the <literal role="hook">changegroup</literal> -hook (section <xref linkend="sec:hook:changegroup"/>); it's simply more convenient -sometimes to run a hook once per group of changesets, while other -times it's handier once per changeset. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>node</literal>: A changeset ID. The ID of the newly added - changeset. -</para> -</listitem> -<listitem><para><literal>source</literal>: A string. The source of these changes. See - section <xref linkend="sec:hook:sources"/> for details. -</para> -</listitem> -<listitem><para><literal>url</literal>: A URL. The location of the remote repository, if - known. See section <xref linkend="sec:hook:url"/> for more information. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">changegroup</literal> (section <xref linkend="sec:hook:changegroup"/>) <literal role="hook">prechangegroup</literal> (section <xref linkend="sec:hook:prechangegroup"/>), <literal role="hook">pretxnchangegroup</literal> (section <xref linkend="sec:hook:pretxnchangegroup"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">outgoing</literal>&emdash;after changesets are propagated</title> -<para>\label{sec:hook:outgoing} -</para> - -<para>This hook is run after a group of changesets has been propagated out -of this repository, for example by a <command role="hg-cmd">hg push</command> or <command role="hg-cmd">hg bundle</command> -command. -</para> - -<para>One possible use for this hook is to notify administrators that -changes have been pulled. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>node</literal>: A changeset ID. The changeset ID of the first - changeset of the group that was sent. -</para> -</listitem> -<listitem><para><literal>source</literal>: A string. The source of the of the operation - (see section <xref linkend="sec:hook:sources"/>). If a remote client pulled - changes from this repository, <literal>source</literal> will be - <literal>serve</literal>. If the client that obtained changes from this - repository was local, <literal>source</literal> will be <literal>bundle</literal>, - <literal>pull</literal>, or <literal>push</literal>, depending on the operation the - client performed. -</para> -</listitem> -<listitem><para><literal>url</literal>: A URL. The location of the remote repository, if - known. See section <xref linkend="sec:hook:url"/> for more information. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">preoutgoing</literal> (section <xref linkend="sec:hook:preoutgoing"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">prechangegroup</literal>&emdash;before starting to add remote changesets</title> -<para>\label{sec:hook:prechangegroup} -</para> - -<para>This controlling hook is run before Mercurial begins to add a group of -changesets from another repository. -</para> - -<para>This hook does not have any information about the changesets to be -added, because it is run before transmission of those changesets is -allowed to begin. If this hook fails, the changesets will not be -transmitted. -</para> - -<para>One use for this hook is to prevent external changes from being added -to a repository. For example, you could use this to <quote>freeze</quote> a -server-hosted branch temporarily or permanently so that users cannot -push to it, while still allowing a local administrator to modify the -repository. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>source</literal>: A string. The source of these changes. See - section <xref linkend="sec:hook:sources"/> for details. -</para> -</listitem> -<listitem><para><literal>url</literal>: A URL. The location of the remote repository, if - known. See section <xref linkend="sec:hook:url"/> for more information. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">changegroup</literal> (section <xref linkend="sec:hook:changegroup"/>), -<literal role="hook">incoming</literal> (section <xref linkend="sec:hook:incoming"/>), , -<literal role="hook">pretxnchangegroup</literal> (section <xref linkend="sec:hook:pretxnchangegroup"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">precommit</literal>&emdash;before starting to commit a changeset</title> -<para>\label{sec:hook:precommit} -</para> - -<para>This hook is run before Mercurial begins to commit a new changeset. -It is run before Mercurial has any of the metadata for the commit, -such as the files to be committed, the commit message, or the commit -date. -</para> - -<para>One use for this hook is to disable the ability to commit new -changesets, while still allowing incoming changesets. Another is to -run a build or test, and only allow the commit to begin if the build -or test succeeds. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>parent1</literal>: A changeset ID. The changeset ID of the first - parent of the working directory. -</para> -</listitem> -<listitem><para><literal>parent2</literal>: A changeset ID. The changeset ID of the second - parent of the working directory. -</para> -</listitem></itemizedlist> -<para>If the commit proceeds, the parents of the working directory will -become the parents of the new changeset. -</para> - -<para>See also: <literal role="hook">commit</literal> (section <xref linkend="sec:hook:commit"/>), -<literal role="hook">pretxncommit</literal> (section <xref linkend="sec:hook:pretxncommit"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">preoutgoing</literal>&emdash;before starting to propagate changesets</title> -<para>\label{sec:hook:preoutgoing} -</para> - -<para>This hook is invoked before Mercurial knows the identities of the -changesets to be transmitted. -</para> - -<para>One use for this hook is to prevent changes from being transmitted to -another repository. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>source</literal>: A string. The source of the operation that is - attempting to obtain changes from this repository (see - section <xref linkend="sec:hook:sources"/>). See the documentation for the - <literal>source</literal> parameter to the <literal role="hook">outgoing</literal> hook, in - section <xref linkend="sec:hook:outgoing"/>, for possible values of this - parameter. -</para> -</listitem> -<listitem><para><literal>url</literal>: A URL. The location of the remote repository, if - known. See section <xref linkend="sec:hook:url"/> for more information. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">outgoing</literal> (section <xref linkend="sec:hook:outgoing"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">pretag</literal>&emdash;before tagging a changeset</title> -<para>\label{sec:hook:pretag} -</para> - -<para>This controlling hook is run before a tag is created. If the hook -succeeds, creation of the tag proceeds. If the hook fails, the tag is -not created. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>local</literal>: A boolean. Whether the tag is local to this - repository instance (i.e. stored in <filename role="special">.hg/localtags</filename>) or - managed by Mercurial (stored in <filename role="special">.hgtags</filename>). -</para> -</listitem> -<listitem><para><literal>node</literal>: A changeset ID. The ID of the changeset to be tagged. -</para> -</listitem> -<listitem><para><literal>tag</literal>: A string. The name of the tag to be created. -</para> -</listitem></itemizedlist> - -<para>If the tag to be created is revision-controlled, the <literal role="hook">precommit</literal> -and <literal role="hook">pretxncommit</literal> hooks (sections <xref linkend="sec:hook:commit"/> -and <xref linkend="sec:hook:pretxncommit"/>) will also be run. -</para> - -<para>See also: <literal role="hook">tag</literal> (section <xref linkend="sec:hook:tag"/>) -</para> - -<para>\subsection{<literal role="hook">pretxnchangegroup</literal>&emdash;before completing addition of - remote changesets} -\label{sec:hook:pretxnchangegroup} -</para> - -<para>This controlling hook is run before a transaction&emdash;that manages the -addition of a group of new changesets from outside the -repository&emdash;completes. If the hook succeeds, the transaction -completes, and all of the changesets become permanent within this -repository. If the hook fails, the transaction is rolled back, and -the data for the changesets is erased. -</para> - -<para>This hook can access the metadata associated with the almost-added -changesets, but it should not do anything permanent with this data. -It must also not modify the working directory. -</para> - -<para>While this hook is running, if other Mercurial processes access this -repository, they will be able to see the almost-added changesets as if -they are permanent. This may lead to race conditions if you do not -take steps to avoid them. -</para> - -<para>This hook can be used to automatically vet a group of changesets. If -the hook fails, all of the changesets are <quote>rejected</quote> when the -transaction rolls back. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>node</literal>: A changeset ID. The changeset ID of the first - changeset in the group that was added. All changesets between this - and \index{tags!<literal>tip</literal>}<literal>tip</literal>, inclusive, were added by - a single <command role="hg-cmd">hg pull</command>, <command role="hg-cmd">hg push</command> or <command role="hg-cmd">hg unbundle</command>. -</para> -</listitem> -<listitem><para><literal>source</literal>: A string. The source of these changes. See - section <xref linkend="sec:hook:sources"/> for details. -</para> -</listitem> -<listitem><para><literal>url</literal>: A URL. The location of the remote repository, if - known. See section <xref linkend="sec:hook:url"/> for more information. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">changegroup</literal> (section <xref linkend="sec:hook:changegroup"/>), -<literal role="hook">incoming</literal> (section <xref linkend="sec:hook:incoming"/>), -<literal role="hook">prechangegroup</literal> (section <xref linkend="sec:hook:prechangegroup"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">pretxncommit</literal>&emdash;before completing commit of new changeset</title> -<para>\label{sec:hook:pretxncommit} -</para> - -<para>This controlling hook is run before a transaction&emdash;that manages a new -commit&emdash;completes. If the hook succeeds, the transaction completes -and the changeset becomes permanent within this repository. If the -hook fails, the transaction is rolled back, and the commit data is -erased. -</para> - -<para>This hook can access the metadata associated with the almost-new -changeset, but it should not do anything permanent with this data. It -must also not modify the working directory. -</para> - -<para>While this hook is running, if other Mercurial processes access this -repository, they will be able to see the almost-new changeset as if it -is permanent. This may lead to race conditions if you do not take -steps to avoid them. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>node</literal>: A changeset ID. The changeset ID of the newly - committed changeset. -</para> -</listitem> -<listitem><para><literal>parent1</literal>: A changeset ID. The changeset ID of the first - parent of the newly committed changeset. -</para> -</listitem> -<listitem><para><literal>parent2</literal>: A changeset ID. The changeset ID of the second - parent of the newly committed changeset. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">precommit</literal> (section <xref linkend="sec:hook:precommit"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">preupdate</literal>&emdash;before updating or merging working directory</title> -<para>\label{sec:hook:preupdate} -</para> - -<para>This controlling hook is run before an update or merge of the working -directory begins. It is run only if Mercurial's normal pre-update -checks determine that the update or merge can proceed. If the hook -succeeds, the update or merge may proceed; if it fails, the update or -merge does not start. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>parent1</literal>: A changeset ID. The ID of the parent that the - working directory is to be updated to. If the working directory is - being merged, it will not change this parent. -</para> -</listitem> -<listitem><para><literal>parent2</literal>: A changeset ID. Only set if the working - directory is being merged. The ID of the revision that the working - directory is being merged with. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">update</literal> (section <xref linkend="sec:hook:update"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">tag</literal>&emdash;after tagging a changeset</title> -<para>\label{sec:hook:tag} -</para> - -<para>This hook is run after a tag has been created. -</para> - -<para>Parameters to this hook: -</para> -<itemizedlist> -<listitem><para><literal>local</literal>: A boolean. Whether the new tag is local to this - repository instance (i.e. stored in <filename role="special">.hg/localtags</filename>) or - managed by Mercurial (stored in <filename role="special">.hgtags</filename>). -</para> -</listitem> -<listitem><para><literal>node</literal>: A changeset ID. The ID of the changeset that was - tagged. -</para> -</listitem> -<listitem><para><literal>tag</literal>: A string. The name of the tag that was created. -</para> -</listitem></itemizedlist> - -<para>If the created tag is revision-controlled, the <literal role="hook">commit</literal> hook -(section <xref linkend="sec:hook:commit"/>) is run before this hook. -</para> - -<para>See also: <literal role="hook">pretag</literal> (section <xref linkend="sec:hook:pretag"/>) -</para> - -</sect2> -<sect2> -<title><literal role="hook">update</literal>&emdash;after updating or merging working directory</title> -<para>\label{sec:hook:update} -</para> - -<para>This hook is run after an update or merge of the working directory -completes. Since a merge can fail (if the external <command>hgmerge</command> -command fails to resolve conflicts in a file), this hook communicates -whether the update or merge completed cleanly. -</para> - -<itemizedlist> -<listitem><para><literal>error</literal>: A boolean. Indicates whether the update or - merge completed successfully. -</para> -</listitem> -<listitem><para><literal>parent1</literal>: A changeset ID. The ID of the parent that the - working directory was updated to. If the working directory was - merged, it will not have changed this parent. -</para> -</listitem> -<listitem><para><literal>parent2</literal>: A changeset ID. Only set if the working - directory was merged. The ID of the revision that the working - directory was merged with. -</para> -</listitem></itemizedlist> - -<para>See also: <literal role="hook">preupdate</literal> (section <xref linkend="sec:hook:preupdate"/>) -</para> - -</sect2> -</sect1> +<chapter id="chap:hook"> + <?dbhtml filename="handling-repository-events-with-hooks.html"?> + <title>Handling repository events with hooks</title> + + <para id="x_1e6">Mercurial offers a powerful mechanism to let you perform + automated actions in response to events that occur in a + repository. In some cases, you can even control Mercurial's + response to those events.</para> + + <para id="x_1e7">The name Mercurial uses for one of these actions is a + <emphasis>hook</emphasis>. Hooks are called + <quote>triggers</quote> in some revision control systems, but the + two names refer to the same idea.</para> + + <sect1> + <title>An overview of hooks in Mercurial</title> + + <para id="x_1e8">Here is a brief list of the hooks that Mercurial + supports. We will revisit each of these hooks in more detail + later, in <xref linkend="sec:hook:ref"/>.</para> + + <para id="x_1f6">Each of the hooks whose description begins with the word + <quote>Controlling</quote> has the ability to determine whether + an activity can proceed. If the hook succeeds, the activity may + proceed; if it fails, the activity is either not permitted or + undone, depending on the hook.</para> + + <itemizedlist> + <listitem><para id="x_1e9"><literal role="hook">changegroup</literal>: This + is run after a group of changesets has been brought into the + repository from elsewhere.</para> + </listitem> + <listitem><para id="x_1ea"><literal role="hook">commit</literal>: This is + run after a new changeset has been created in the local + repository.</para> + </listitem> + <listitem><para id="x_1eb"><literal role="hook">incoming</literal>: This is + run once for each new changeset that is brought into the + repository from elsewhere. Notice the difference from + <literal role="hook">changegroup</literal>, which is run + once per <emphasis>group</emphasis> of changesets brought + in.</para> + </listitem> + <listitem><para id="x_1ec"><literal role="hook">outgoing</literal>: This is + run after a group of changesets has been transmitted from + this repository.</para> + </listitem> + <listitem><para id="x_1ed"><literal role="hook">prechangegroup</literal>: + This is run before starting to bring a group of changesets + into the repository. + </para> + </listitem> + <listitem><para id="x_1ee"><literal role="hook">precommit</literal>: + Controlling. This is run before starting a commit. + </para> + </listitem> + <listitem><para id="x_1ef"><literal role="hook">preoutgoing</literal>: + Controlling. This is run before starting to transmit a group + of changesets from this repository. + </para> + </listitem> + <listitem><para id="x_1f0"><literal role="hook">pretag</literal>: + Controlling. This is run before creating a tag. + </para> + </listitem> + <listitem><para id="x_1f1"><literal + role="hook">pretxnchangegroup</literal>: Controlling. This + is run after a group of changesets has been brought into the + local repository from another, but before the transaction + completes that will make the changes permanent in the + repository. + </para> + </listitem> + <listitem><para id="x_1f2"><literal role="hook">pretxncommit</literal>: + Controlling. This is run after a new changeset has been + created in the local repository, but before the transaction + completes that will make it permanent. + </para> + </listitem> + <listitem><para id="x_1f3"><literal role="hook">preupdate</literal>: + Controlling. This is run before starting an update or merge + of the working directory. + </para> + </listitem> + <listitem><para id="x_1f4"><literal role="hook">tag</literal>: This is run + after a tag is created. + </para> + </listitem> + <listitem><para id="x_1f5"><literal role="hook">update</literal>: This is + run after an update or merge of the working directory has + finished. + </para> + </listitem></itemizedlist> + + </sect1> + <sect1> + <title>Hooks and security</title> + + <sect2> + <title>Hooks are run with your privileges</title> + + <para id="x_1f7">When you run a Mercurial command in a repository, and the + command causes a hook to run, that hook runs on + <emphasis>your</emphasis> system, under + <emphasis>your</emphasis> user account, with + <emphasis>your</emphasis> privilege level. Since hooks are + arbitrary pieces of executable code, you should treat them + with an appropriate level of suspicion. Do not install a hook + unless you are confident that you know who created it and what + it does. + </para> + + <para id="x_1f8">In some cases, you may be exposed to hooks that you did + not install yourself. If you work with Mercurial on an + unfamiliar system, Mercurial will run hooks defined in that + system's global <filename role="special">~/.hgrc</filename> + file. + </para> + + <para id="x_1f9">If you are working with a repository owned by another + user, Mercurial can run hooks defined in that user's + repository, but it will still run them as <quote>you</quote>. + For example, if you <command role="hg-cmd">hg pull</command> + from that repository, and its <filename + role="special">.hg/hgrc</filename> defines a local <literal + role="hook">outgoing</literal> hook, that hook will run + under your user account, even though you don't own that + repository. + </para> + + <note> + <para id="x_1fa"> This only applies if you are pulling from a repository + on a local or network filesystem. If you're pulling over + http or ssh, any <literal role="hook">outgoing</literal> + hook will run under whatever account is executing the server + process, on the server. + </para> + </note> + + <para id="x_1fb">To see what hooks are defined in a repository, + use the <command role="hg-cmd">hg showconfig hooks</command> + command. If you are working in one repository, but talking to + another that you do not own (e.g. using <command + role="hg-cmd">hg pull</command> or <command role="hg-cmd">hg + incoming</command>), remember that it is the other + repository's hooks you should be checking, not your own. + </para> + </sect2> + + <sect2> + <title>Hooks do not propagate</title> + + <para id="x_1fc">In Mercurial, hooks are not revision controlled, and do + not propagate when you clone, or pull from, a repository. The + reason for this is simple: a hook is a completely arbitrary + piece of executable code. It runs under your user identity, + with your privilege level, on your machine. + </para> + + <para id="x_1fd">It would be extremely reckless for any distributed + revision control system to implement revision-controlled + hooks, as this would offer an easily exploitable way to + subvert the accounts of users of the revision control system. + </para> + + <para id="x_1fe">Since Mercurial does not propagate hooks, if you are + collaborating with other people on a common project, you + should not assume that they are using the same Mercurial hooks + as you are, or that theirs are correctly configured. You + should document the hooks you expect people to use. + </para> + + <para id="x_1ff">In a corporate intranet, this is somewhat easier to + control, as you can for example provide a + <quote>standard</quote> installation of Mercurial on an NFS + filesystem, and use a site-wide <filename role="special">~/.hgrc</filename> file to define hooks that all users will + see. However, this too has its limits; see below. + </para> + </sect2> + + <sect2> + <title>Hooks can be overridden</title> + + <para id="x_200">Mercurial allows you to override a hook definition by + redefining the hook. You can disable it by setting its value + to the empty string, or change its behavior as you wish. + </para> + + <para id="x_201">If you deploy a system- or site-wide <filename + role="special">~/.hgrc</filename> file that defines some + hooks, you should thus understand that your users can disable + or override those hooks. + </para> + </sect2> + + <sect2> + <title>Ensuring that critical hooks are run</title> + + <para id="x_202">Sometimes you may want to enforce a policy that you do not + want others to be able to work around. For example, you may + have a requirement that every changeset must pass a rigorous + set of tests. Defining this requirement via a hook in a + site-wide <filename role="special">~/.hgrc</filename> won't + work for remote users on laptops, and of course local users + can subvert it at will by overriding the hook. + </para> + + <para id="x_203">Instead, you can set up your policies for use of Mercurial + so that people are expected to propagate changes through a + well-known <quote>canonical</quote> server that you have + locked down and configured appropriately. + </para> + + <para id="x_204">One way to do this is via a combination of social + engineering and technology. Set up a restricted-access + account; users can push changes over the network to + repositories managed by this account, but they cannot log into + the account and run normal shell commands. In this scenario, + a user can commit a changeset that contains any old garbage + they want. + </para> + + <para id="x_205">When someone pushes a changeset to the server that + everyone pulls from, the server will test the changeset before + it accepts it as permanent, and reject it if it fails to pass + the test suite. If people only pull changes from this + filtering server, it will serve to ensure that all changes + that people pull have been automatically vetted. + </para> + + </sect2> + </sect1> + + <sect1 id="sec:hook:simple"> + <title>A short tutorial on using hooks</title> + + <para id="x_212">It is easy to write a Mercurial hook. Let's start with a + hook that runs when you finish a <command role="hg-cmd">hg + commit</command>, and simply prints the hash of the changeset + you just created. The hook is called <literal + role="hook">commit</literal>. + </para> + + <para id="x_213">All hooks follow the pattern in this example.</para> + +&interaction.hook.simple.init; + + <para id="x_214">You add an entry to the <literal + role="rc-hooks">hooks</literal> section of your <filename + role="special">~/.hgrc</filename>. On the left is the name of + the event to trigger on; on the right is the action to take. As + you can see, you can run an arbitrary shell command in a hook. + Mercurial passes extra information to the hook using environment + variables (look for <envar>HG_NODE</envar> in the example). + </para> + + <sect2> + <title>Performing multiple actions per event</title> + + <para id="x_215">Quite often, you will want to define more than one hook + for a particular kind of event, as shown below.</para> + +&interaction.hook.simple.ext; + + <para id="x_216">Mercurial lets you do this by adding an + <emphasis>extension</emphasis> to the end of a hook's name. + You extend a hook's name by giving the name of the hook, + followed by a full stop (the + <quote><literal>.</literal></quote> character), followed by + some more text of your choosing. For example, Mercurial will + run both <literal>commit.foo</literal> and + <literal>commit.bar</literal> when the + <literal>commit</literal> event occurs. + </para> + + <para id="x_217">To give a well-defined order of execution when there are + multiple hooks defined for an event, Mercurial sorts hooks by + extension, and executes the hook commands in this sorted + order. In the above example, it will execute + <literal>commit.bar</literal> before + <literal>commit.foo</literal>, and <literal>commit</literal> + before both. + </para> + + <para id="x_218">It is a good idea to use a somewhat descriptive + extension when you define a new hook. This will help you to + remember what the hook was for. If the hook fails, you'll get + an error message that contains the hook name and extension, so + using a descriptive extension could give you an immediate hint + as to why the hook failed (see <xref + linkend="sec:hook:perm"/> for an example). + </para> + + </sect2> + <sect2 id="sec:hook:perm"> + <title>Controlling whether an activity can proceed</title> + + <para id="x_219">In our earlier examples, we used the <literal + role="hook">commit</literal> hook, which is run after a + commit has completed. This is one of several Mercurial hooks + that run after an activity finishes. Such hooks have no way + of influencing the activity itself. + </para> + + <para id="x_21a">Mercurial defines a number of events that occur before an + activity starts; or after it starts, but before it finishes. + Hooks that trigger on these events have the added ability to + choose whether the activity can continue, or will abort. + </para> + + <para id="x_21b">The <literal role="hook">pretxncommit</literal> hook runs + after a commit has all but completed. In other words, the + metadata representing the changeset has been written out to + disk, but the transaction has not yet been allowed to + complete. The <literal role="hook">pretxncommit</literal> + hook has the ability to decide whether the transaction can + complete, or must be rolled back. + </para> + + <para id="x_21c">If the <literal role="hook">pretxncommit</literal> hook + exits with a status code of zero, the transaction is allowed + to complete; the commit finishes; and the <literal + role="hook">commit</literal> hook is run. If the <literal + role="hook">pretxncommit</literal> hook exits with a + non-zero status code, the transaction is rolled back; the + metadata representing the changeset is erased; and the + <literal role="hook">commit</literal> hook is not run. + </para> + +&interaction.hook.simple.pretxncommit; + + <para id="x_21d">The hook in the example above checks that a commit comment + contains a bug ID. If it does, the commit can complete. If + not, the commit is rolled back. + </para> + + </sect2> + </sect1> + <sect1> + <title>Writing your own hooks</title> + + <para id="x_21e">When you are writing a hook, you might find it useful to run + Mercurial either with the <option + role="hg-opt-global">-v</option> option, or the <envar + role="rc-item-ui">verbose</envar> config item set to + <quote>true</quote>. When you do so, Mercurial will print a + message before it calls each hook. + </para> + + <sect2 id="sec:hook:lang"> + <title>Choosing how your hook should run</title> + + <para id="x_21f">You can write a hook either as a normal + program&emdash;typically a shell script&emdash;or as a Python + function that is executed within the Mercurial process. + </para> + + <para id="x_220">Writing a hook as an external program has the advantage + that it requires no knowledge of Mercurial's internals. You + can call normal Mercurial commands to get any added + information you need. The trade-off is that external hooks + are slower than in-process hooks. + </para> + + <para id="x_221">An in-process Python hook has complete access to the + Mercurial API, and does not <quote>shell out</quote> to + another process, so it is inherently faster than an external + hook. It is also easier to obtain much of the information + that a hook requires by using the Mercurial API than by + running Mercurial commands. + </para> + + <para id="x_222">If you are comfortable with Python, or require high + performance, writing your hooks in Python may be a good + choice. However, when you have a straightforward hook to + write and you don't need to care about performance (probably + the majority of hooks), a shell script is perfectly fine. + </para> + + </sect2> + <sect2 id="sec:hook:param"> + <title>Hook parameters</title> + + <para id="x_223">Mercurial calls each hook with a set of well-defined + parameters. In Python, a parameter is passed as a keyword + argument to your hook function. For an external program, a + parameter is passed as an environment variable. + </para> + + <para id="x_224">Whether your hook is written in Python or as a shell + script, the hook-specific parameter names and values will be + the same. A boolean parameter will be represented as a + boolean value in Python, but as the number 1 (for + <quote>true</quote>) or 0 (for <quote>false</quote>) as an + environment variable for an external hook. If a hook + parameter is named <literal>foo</literal>, the keyword + argument for a Python hook will also be named + <literal>foo</literal>, while the environment variable for an + external hook will be named <literal>HG_FOO</literal>. + </para> + </sect2> + + <sect2> + <title>Hook return values and activity control</title> + + <para id="x_225">A hook that executes successfully must exit with a status + of zero if external, or return boolean <quote>false</quote> if + in-process. Failure is indicated with a non-zero exit status + from an external hook, or an in-process hook returning boolean + <quote>true</quote>. If an in-process hook raises an + exception, the hook is considered to have failed. + </para> + + <para id="x_226">For a hook that controls whether an activity can proceed, + zero/false means <quote>allow</quote>, while + non-zero/true/exception means <quote>deny</quote>. + </para> + </sect2> + + <sect2> + <title>Writing an external hook</title> + + <para id="x_227">When you define an external hook in your <filename + role="special">~/.hgrc</filename> and the hook is run, its + value is passed to your shell, which interprets it. This + means that you can use normal shell constructs in the body of + the hook. + </para> + + <para id="x_228">An executable hook is always run with its current + directory set to a repository's root directory. + </para> + + <para id="x_229">Each hook parameter is passed in as an environment + variable; the name is upper-cased, and prefixed with the + string <quote><literal>HG_</literal></quote>. + </para> + + <para id="x_22a">With the exception of hook parameters, Mercurial does not + set or modify any environment variables when running a hook. + This is useful to remember if you are writing a site-wide hook + that may be run by a number of different users with differing + environment variables set. In multi-user situations, you + should not rely on environment variables being set to the + values you have in your environment when testing the hook. + </para> + </sect2> + + <sect2> + <title>Telling Mercurial to use an in-process hook</title> + + <para id="x_22b">The <filename role="special">~/.hgrc</filename> syntax + for defining an in-process hook is slightly different than for + an executable hook. The value of the hook must start with the + text <quote><literal>python:</literal></quote>, and continue + with the fully-qualified name of a callable object to use as + the hook's value. + </para> + + <para id="x_22c">The module in which a hook lives is automatically imported + when a hook is run. So long as you have the module name and + <envar>PYTHONPATH</envar> right, it should <quote>just + work</quote>. + </para> + + <para id="x_22d">The following <filename role="special">~/.hgrc</filename> + example snippet illustrates the syntax and meaning of the + notions we just described. + </para> + <programlisting>[hooks] +commit.example = python:mymodule.submodule.myhook</programlisting> + <para id="x_22e">When Mercurial runs the <literal>commit.example</literal> + hook, it imports <literal>mymodule.submodule</literal>, looks + for the callable object named <literal>myhook</literal>, and + calls it. + </para> + </sect2> + + <sect2> + <title>Writing an in-process hook</title> + + <para id="x_22f">The simplest in-process hook does nothing, but illustrates + the basic shape of the hook API: + </para> + <programlisting>def myhook(ui, repo, **kwargs): + pass</programlisting> + <para id="x_230">The first argument to a Python hook is always a <literal + role="py-mod-mercurial.ui">ui</literal> object. The second + is a repository object; at the moment, it is always an + instance of <literal + role="py-mod-mercurial.localrepo">localrepository</literal>. + Following these two arguments are other keyword arguments. + Which ones are passed in depends on the hook being called, but + a hook can ignore arguments it doesn't care about by dropping + them into a keyword argument dict, as with + <literal>**kwargs</literal> above. + </para> + + </sect2> + </sect1> + <sect1> + <title>Some hook examples</title> + + <sect2> + <title>Writing meaningful commit messages</title> + + <para id="x_231">It's hard to imagine a useful commit message being very + short. The simple <literal role="hook">pretxncommit</literal> + hook of the example below will prevent you from committing a + changeset with a message that is less than ten bytes long. + </para> + +&interaction.hook.msglen.go; + </sect2> + + <sect2> + <title>Checking for trailing whitespace</title> + + <para id="x_232">An interesting use of a commit-related hook is to help you + to write cleaner code. A simple example of <quote>cleaner + code</quote> is the dictum that a change should not add any + new lines of text that contain <quote>trailing + whitespace</quote>. Trailing whitespace is a series of + space and tab characters at the end of a line of text. In + most cases, trailing whitespace is unnecessary, invisible + noise, but it is occasionally problematic, and people often + prefer to get rid of it. + </para> + + <para id="x_233">You can use either the <literal + role="hook">precommit</literal> or <literal + role="hook">pretxncommit</literal> hook to tell whether you + have a trailing whitespace problem. If you use the <literal + role="hook">precommit</literal> hook, the hook will not know + which files you are committing, so it will have to check every + modified file in the repository for trailing white space. If + you want to commit a change to just the file + <filename>foo</filename>, but the file + <filename>bar</filename> contains trailing whitespace, doing a + check in the <literal role="hook">precommit</literal> hook + will prevent you from committing <filename>foo</filename> due + to the problem with <filename>bar</filename>. This doesn't + seem right. + </para> + + <para id="x_234">Should you choose the <literal + role="hook">pretxncommit</literal> hook, the check won't + occur until just before the transaction for the commit + completes. This will allow you to check for problems only the + exact files that are being committed. However, if you entered + the commit message interactively and the hook fails, the + transaction will roll back; you'll have to re-enter the commit + message after you fix the trailing whitespace and run <command + role="hg-cmd">hg commit</command> again. + </para> + + &interaction.ch09-hook.ws.simple; + + <para id="x_235">In this example, we introduce a simple <literal + role="hook">pretxncommit</literal> hook that checks for + trailing whitespace. This hook is short, but not very + helpful. It exits with an error status if a change adds a + line with trailing whitespace to any file, but does not print + any information that might help us to identify the offending + file or line. It also has the nice property of not paying + attention to unmodified lines; only lines that introduce new + trailing whitespace cause problems. + </para> + + &ch09-check_whitespace.py.lst; + + <para id="x_236">The above version is much more complex, but also more + useful. It parses a unified diff to see if any lines add + trailing whitespace, and prints the name of the file and the + line number of each such occurrence. Even better, if the + change adds trailing whitespace, this hook saves the commit + comment and prints the name of the save file before exiting + and telling Mercurial to roll the transaction back, so you can + use the <option role="hg-opt-commit">-l filename</option> + option to <command role="hg-cmd">hg commit</command> to reuse + the saved commit message once you've corrected the problem. + </para> + + &interaction.ch09-hook.ws.better; + + <para id="x_237">As a final aside, note in the example above the + use of <command>sed</command>'s in-place editing feature to + get rid of trailing whitespace from a file. This is concise + and useful enough that I will reproduce it here (using + <command>perl</command> for good measure).</para> + <programlisting>perl -pi -e 's,\s+$,,' filename</programlisting> + + </sect2> + </sect1> + <sect1> + <title>Bundled hooks</title> + + <para id="x_238">Mercurial ships with several bundled hooks. You can find + them in the <filename class="directory">hgext</filename> + directory of a Mercurial source tree. If you are using a + Mercurial binary package, the hooks will be located in the + <filename class="directory">hgext</filename> directory of + wherever your package installer put Mercurial. + </para> + + <sect2> + <title><literal role="hg-ext">acl</literal>&emdash;access + control for parts of a repository</title> + + <para id="x_239">The <literal role="hg-ext">acl</literal> extension lets + you control which remote users are allowed to push changesets + to a networked server. You can protect any portion of a + repository (including the entire repo), so that a specific + remote user can push changes that do not affect the protected + portion. + </para> + + <para id="x_23a">This extension implements access control based on the + identity of the user performing a push, + <emphasis>not</emphasis> on who committed the changesets + they're pushing. It makes sense to use this hook only if you + have a locked-down server environment that authenticates + remote users, and you want to be sure that only specific users + are allowed to push changes to that server. + </para> + + <sect3> + <title>Configuring the <literal role="hook">acl</literal> + hook</title> + + <para id="x_23b">In order to manage incoming changesets, the <literal + role="hg-ext">acl</literal> hook must be used as a + <literal role="hook">pretxnchangegroup</literal> hook. This + lets it see which files are modified by each incoming + changeset, and roll back a group of changesets if they + modify <quote>forbidden</quote> files. Example: + </para> + <programlisting>[hooks] +pretxnchangegroup.acl = python:hgext.acl.hook</programlisting> + + <para id="x_23c">The <literal role="hg-ext">acl</literal> extension is + configured using three sections. + </para> + + <para id="x_23d">The <literal role="rc-acl">acl</literal> section has + only one entry, <envar role="rc-item-acl">sources</envar>, + which lists the sources of incoming changesets that the hook + should pay attention to. You don't normally need to + configure this section. + </para> + <itemizedlist> + <listitem><para id="x_23e"><envar role="rc-item-acl">serve</envar>: + Control incoming changesets that are arriving from a + remote repository over http or ssh. This is the default + value of <envar role="rc-item-acl">sources</envar>, and + usually the only setting you'll need for this + configuration item. + </para> + </listitem> + <listitem><para id="x_23f"><envar role="rc-item-acl">pull</envar>: + Control incoming changesets that are arriving via a pull + from a local repository. + </para> + </listitem> + <listitem><para id="x_240"><envar role="rc-item-acl">push</envar>: + Control incoming changesets that are arriving via a push + from a local repository. + </para> + </listitem> + <listitem><para id="x_241"><envar role="rc-item-acl">bundle</envar>: + Control incoming changesets that are arriving from + another repository via a bundle. + </para> + </listitem></itemizedlist> + + <para id="x_242">The <literal role="rc-acl.allow">acl.allow</literal> + section controls the users that are allowed to add + changesets to the repository. If this section is not + present, all users that are not explicitly denied are + allowed. If this section is present, all users that are not + explicitly allowed are denied (so an empty section means + that all users are denied). + </para> + + <para id="x_243">The <literal role="rc-acl.deny">acl.deny</literal> + section determines which users are denied from adding + changesets to the repository. If this section is not + present or is empty, no users are denied. + </para> + + <para id="x_244">The syntaxes for the <literal + role="rc-acl.allow">acl.allow</literal> and <literal + role="rc-acl.deny">acl.deny</literal> sections are + identical. On the left of each entry is a glob pattern that + matches files or directories, relative to the root of the + repository; on the right, a user name. + </para> + + <para id="x_245">In the following example, the user + <literal>docwriter</literal> can only push changes to the + <filename class="directory">docs</filename> subtree of the + repository, while <literal>intern</literal> can push changes + to any file or directory except <filename + class="directory">source/sensitive</filename>. + </para> + <programlisting>[acl.allow] +docs/** = docwriter +[acl.deny] +source/sensitive/** = intern</programlisting> + + </sect3> + <sect3> + <title>Testing and troubleshooting</title> + + <para id="x_246">If you want to test the <literal + role="hg-ext">acl</literal> hook, run it with Mercurial's + debugging output enabled. Since you'll probably be running + it on a server where it's not convenient (or sometimes + possible) to pass in the <option + role="hg-opt-global">--debug</option> option, don't forget + that you can enable debugging output in your <filename + role="special">~/.hgrc</filename>: + </para> + <programlisting>[ui] +debug = true</programlisting> + <para id="x_247">With this enabled, the <literal + role="hg-ext">acl</literal> hook will print enough + information to let you figure out why it is allowing or + forbidding pushes from specific users. + </para> + + </sect3> </sect2> + + <sect2> + <title><literal + role="hg-ext">bugzilla</literal>&emdash;integration with + Bugzilla</title> + + <para id="x_248">The <literal role="hg-ext">bugzilla</literal> extension + adds a comment to a Bugzilla bug whenever it finds a reference + to that bug ID in a commit comment. You can install this hook + on a shared server, so that any time a remote user pushes + changes to this server, the hook gets run. + </para> + + <para id="x_249">It adds a comment to the bug that looks like this (you can + configure the contents of the comment&emdash;see below): + </para> + <programlisting>Changeset aad8b264143a, made by Joe User + <joe.user@domain.com> in the frobnitz repository, refers + to this bug. For complete details, see + http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a + Changeset description: Fix bug 10483 by guarding against some + NULL pointers</programlisting> + <para id="x_24a">The value of this hook is that it automates the process of + updating a bug any time a changeset refers to it. If you + configure the hook properly, it makes it easy for people to + browse straight from a Bugzilla bug to a changeset that refers + to that bug. + </para> + + <para id="x_24b">You can use the code in this hook as a starting point for + some more exotic Bugzilla integration recipes. Here are a few + possibilities: + </para> + <itemizedlist> + <listitem><para id="x_24c">Require that every changeset pushed to the + server have a valid bug ID in its commit comment. In this + case, you'd want to configure the hook as a <literal + role="hook">pretxncommit</literal> hook. This would + allow the hook to reject changes that didn't contain bug + IDs. + </para> + </listitem> + <listitem><para id="x_24d">Allow incoming changesets to automatically + modify the <emphasis>state</emphasis> of a bug, as well as + simply adding a comment. For example, the hook could + recognise the string <quote>fixed bug 31337</quote> as + indicating that it should update the state of bug 31337 to + <quote>requires testing</quote>. + </para> + </listitem></itemizedlist> + + <sect3 id="sec:hook:bugzilla:config"> + <title>Configuring the <literal role="hook">bugzilla</literal> + hook</title> + + <para id="x_24e">You should configure this hook in your server's + <filename role="special">~/.hgrc</filename> as an <literal + role="hook">incoming</literal> hook, for example as + follows: + </para> + <programlisting>[hooks] +incoming.bugzilla = python:hgext.bugzilla.hook</programlisting> + + <para id="x_24f">Because of the specialised nature of this hook, and + because Bugzilla was not written with this kind of + integration in mind, configuring this hook is a somewhat + involved process. + </para> + + <para id="x_250">Before you begin, you must install the MySQL bindings + for Python on the host(s) where you'll be running the hook. + If this is not available as a binary package for your + system, you can download it from + <citation>web:mysql-python</citation>. + </para> + + <para id="x_251">Configuration information for this hook lives in the + <literal role="rc-bugzilla">bugzilla</literal> section of + your <filename role="special">~/.hgrc</filename>. + </para> + <itemizedlist> + <listitem><para id="x_252"><envar + role="rc-item-bugzilla">version</envar>: The version + of Bugzilla installed on the server. The database + schema that Bugzilla uses changes occasionally, so this + hook has to know exactly which schema to use.</para> + </listitem> + <listitem><para id="x_253"><envar role="rc-item-bugzilla">host</envar>: + The hostname of the MySQL server that stores your + Bugzilla data. The database must be configured to allow + connections from whatever host you are running the + <literal role="hook">bugzilla</literal> hook on. + </para> + </listitem> + <listitem><para id="x_254"><envar role="rc-item-bugzilla">user</envar>: + The username with which to connect to the MySQL server. + The database must be configured to allow this user to + connect from whatever host you are running the <literal + role="hook">bugzilla</literal> hook on. This user + must be able to access and modify Bugzilla tables. The + default value of this item is <literal>bugs</literal>, + which is the standard name of the Bugzilla user in a + MySQL database. + </para> + </listitem> + <listitem><para id="x_255"><envar + role="rc-item-bugzilla">password</envar>: The MySQL + password for the user you configured above. This is + stored as plain text, so you should make sure that + unauthorised users cannot read the <filename + role="special">~/.hgrc</filename> file where you + store this information. + </para> + </listitem> + <listitem><para id="x_256"><envar role="rc-item-bugzilla">db</envar>: + The name of the Bugzilla database on the MySQL server. + The default value of this item is + <literal>bugs</literal>, which is the standard name of + the MySQL database where Bugzilla stores its data. + </para> + </listitem> + <listitem><para id="x_257"><envar + role="rc-item-bugzilla">notify</envar>: If you want + Bugzilla to send out a notification email to subscribers + after this hook has added a comment to a bug, you will + need this hook to run a command whenever it updates the + database. The command to run depends on where you have + installed Bugzilla, but it will typically look something + like this, if you have Bugzilla installed in <filename + class="directory">/var/www/html/bugzilla</filename>: + </para> + <programlisting>cd /var/www/html/bugzilla && + ./processmail %s nobody@nowhere.com</programlisting> + </listitem> + <listitem><para id="x_258"> The Bugzilla + <literal>processmail</literal> program expects to be + given a bug ID (the hook replaces + <quote><literal>%s</literal></quote> with the bug ID) + and an email address. It also expects to be able to + write to some files in the directory that it runs in. + If Bugzilla and this hook are not installed on the same + machine, you will need to find a way to run + <literal>processmail</literal> on the server where + Bugzilla is installed. + </para> + </listitem></itemizedlist> + + </sect3> + <sect3> + <title>Mapping committer names to Bugzilla user names</title> + + <para id="x_259">By default, the <literal + role="hg-ext">bugzilla</literal> hook tries to use the + email address of a changeset's committer as the Bugzilla + user name with which to update a bug. If this does not suit + your needs, you can map committer email addresses to + Bugzilla user names using a <literal + role="rc-usermap">usermap</literal> section. + </para> + + <para id="x_25a">Each item in the <literal + role="rc-usermap">usermap</literal> section contains an + email address on the left, and a Bugzilla user name on the + right. + </para> + <programlisting>[usermap] +jane.user@example.com = jane</programlisting> + <para id="x_25b">You can either keep the <literal + role="rc-usermap">usermap</literal> data in a normal + <filename role="special">~/.hgrc</filename>, or tell the + <literal role="hg-ext">bugzilla</literal> hook to read the + information from an external <filename>usermap</filename> + file. In the latter case, you can store + <filename>usermap</filename> data by itself in (for example) + a user-modifiable repository. This makes it possible to let + your users maintain their own <envar + role="rc-item-bugzilla">usermap</envar> entries. The main + <filename role="special">~/.hgrc</filename> file might look + like this: + </para> + <programlisting># regular hgrc file refers to external usermap file +[bugzilla] +usermap = /home/hg/repos/userdata/bugzilla-usermap.conf</programlisting> + <para id="x_25c">While the <filename>usermap</filename> file that it + refers to might look like this: + </para> + <programlisting># bugzilla-usermap.conf - inside a hg repository +[usermap] stephanie@example.com = steph</programlisting> + + </sect3> + <sect3> + <title>Configuring the text that gets added to a bug</title> + + <para id="x_25d">You can configure the text that this hook adds as a + comment; you specify it in the form of a Mercurial template. + Several <filename role="special">~/.hgrc</filename> entries + (still in the <literal role="rc-bugzilla">bugzilla</literal> + section) control this behavior. + </para> + <itemizedlist> + <listitem><para id="x_25e"><literal>strip</literal>: The number of + leading path elements to strip from a repository's path + name to construct a partial path for a URL. For example, + if the repositories on your server live under <filename + class="directory">/home/hg/repos</filename>, and you + have a repository whose path is <filename + class="directory">/home/hg/repos/app/tests</filename>, + then setting <literal>strip</literal> to + <literal>4</literal> will give a partial path of + <filename class="directory">app/tests</filename>. The + hook will make this partial path available when + expanding a template, as <literal>webroot</literal>. + </para> + </listitem> + <listitem><para id="x_25f"><literal>template</literal>: The text of the + template to use. In addition to the usual + changeset-related variables, this template can use + <literal>hgweb</literal> (the value of the + <literal>hgweb</literal> configuration item above) and + <literal>webroot</literal> (the path constructed using + <literal>strip</literal> above). + </para> + </listitem></itemizedlist> + + <para id="x_260">In addition, you can add a <envar + role="rc-item-web">baseurl</envar> item to the <literal + role="rc-web">web</literal> section of your <filename + role="special">~/.hgrc</filename>. The <literal + role="hg-ext">bugzilla</literal> hook will make this + available when expanding a template, as the base string to + use when constructing a URL that will let users browse from + a Bugzilla comment to view a changeset. Example: + </para> + <programlisting>[web] +baseurl = http://hg.domain.com/</programlisting> + + <para id="x_261">Here is an example set of <literal + role="hg-ext">bugzilla</literal> hook config information. + </para> + + &ch10-bugzilla-config.lst; + + </sect3> + <sect3> + <title>Testing and troubleshooting</title> + + <para id="x_262">The most common problems with configuring the <literal + role="hg-ext">bugzilla</literal> hook relate to running + Bugzilla's <filename>processmail</filename> script and + mapping committer names to user names. + </para> + + <para id="x_263">Recall from <xref + linkend="sec:hook:bugzilla:config"/> above that the user + that runs the Mercurial process on the server is also the + one that will run the <filename>processmail</filename> + script. The <filename>processmail</filename> script + sometimes causes Bugzilla to write to files in its + configuration directory, and Bugzilla's configuration files + are usually owned by the user that your web server runs + under. + </para> + + <para id="x_264">You can cause <filename>processmail</filename> to be run + with the suitable user's identity using the + <command>sudo</command> command. Here is an example entry + for a <filename>sudoers</filename> file. + </para> + <programlisting>hg_user = (httpd_user) +NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s</programlisting> + <para id="x_265">This allows the <literal>hg_user</literal> user to run a + <filename>processmail-wrapper</filename> program under the + identity of <literal>httpd_user</literal>. + </para> + + <para id="x_266">This indirection through a wrapper script is necessary, + because <filename>processmail</filename> expects to be run + with its current directory set to wherever you installed + Bugzilla; you can't specify that kind of constraint in a + <filename>sudoers</filename> file. The contents of the + wrapper script are simple: + </para> + <programlisting>#!/bin/sh +cd `dirname $0` && ./processmail "$1" nobody@example.com</programlisting> + <para id="x_267">It doesn't seem to matter what email address you pass to + <filename>processmail</filename>. + </para> + + <para id="x_268">If your <literal role="rc-usermap">usermap</literal> is + not set up correctly, users will see an error message from + the <literal role="hg-ext">bugzilla</literal> hook when they + push changes to the server. The error message will look + like this: + </para> + <programlisting>cannot find bugzilla user id for john.q.public@example.com</programlisting> + <para id="x_269">What this means is that the committer's address, + <literal>john.q.public@example.com</literal>, is not a valid + Bugzilla user name, nor does it have an entry in your + <literal role="rc-usermap">usermap</literal> that maps it to + a valid Bugzilla user name. + </para> + + </sect3> </sect2> + + <sect2> + <title><literal role="hg-ext">notify</literal>&emdash;send email + notifications</title> + + <para id="x_26a">Although Mercurial's built-in web server provides RSS + feeds of changes in every repository, many people prefer to + receive change notifications via email. The <literal + role="hg-ext">notify</literal> hook lets you send out + notifications to a set of email addresses whenever changesets + arrive that those subscribers are interested in. + </para> + + <para id="x_26b">As with the <literal role="hg-ext">bugzilla</literal> + hook, the <literal role="hg-ext">notify</literal> hook is + template-driven, so you can customise the contents of the + notification messages that it sends. + </para> + + <para id="x_26c">By default, the <literal role="hg-ext">notify</literal> + hook includes a diff of every changeset that it sends out; you + can limit the size of the diff, or turn this feature off + entirely. It is useful for letting subscribers review changes + immediately, rather than clicking to follow a URL. + </para> + + <sect3> + <title>Configuring the <literal role="hg-ext">notify</literal> + hook</title> + + <para id="x_26d">You can set up the <literal + role="hg-ext">notify</literal> hook to send one email + message per incoming changeset, or one per incoming group of + changesets (all those that arrived in a single pull or + push). + </para> + <programlisting>[hooks] +# send one email per group of changes +changegroup.notify = python:hgext.notify.hook +# send one email per change +incoming.notify = python:hgext.notify.hook</programlisting> + + <para id="x_26e">Configuration information for this hook lives in the + <literal role="rc-notify">notify</literal> section of a + <filename role="special">~/.hgrc</filename> file. + </para> + <itemizedlist> + <listitem><para id="x_26f"><envar role="rc-item-notify">test</envar>: + By default, this hook does not send out email at all; + instead, it prints the message that it + <emphasis>would</emphasis> send. Set this item to + <literal>false</literal> to allow email to be sent. The + reason that sending of email is turned off by default is + that it takes several tries to configure this extension + exactly as you would like, and it would be bad form to + spam subscribers with a number of <quote>broken</quote> + notifications while you debug your configuration. + </para> + </listitem> + <listitem><para id="x_270"><envar role="rc-item-notify">config</envar>: + The path to a configuration file that contains + subscription information. This is kept separate from + the main <filename role="special">~/.hgrc</filename> so + that you can maintain it in a repository of its own. + People can then clone that repository, update their + subscriptions, and push the changes back to your server. + </para> + </listitem> + <listitem><para id="x_271"><envar role="rc-item-notify">strip</envar>: + The number of leading path separator characters to strip + from a repository's path, when deciding whether a + repository has subscribers. For example, if the + repositories on your server live in <filename + class="directory">/home/hg/repos</filename>, and + <literal role="hg-ext">notify</literal> is considering a + repository named <filename + class="directory">/home/hg/repos/shared/test</filename>, + setting <envar role="rc-item-notify">strip</envar> to + <literal>4</literal> will cause <literal + role="hg-ext">notify</literal> to trim the path it + considers down to <filename + class="directory">shared/test</filename>, and it will + match subscribers against that. + </para> + </listitem> + <listitem><para id="x_272"><envar + role="rc-item-notify">template</envar>: The template + text to use when sending messages. This specifies both + the contents of the message header and its body. + </para> + </listitem> + <listitem><para id="x_273"><envar + role="rc-item-notify">maxdiff</envar>: The maximum + number of lines of diff data to append to the end of a + message. If a diff is longer than this, it is + truncated. By default, this is set to 300. Set this to + <literal>0</literal> to omit diffs from notification + emails. + </para> + </listitem> + <listitem><para id="x_274"><envar + role="rc-item-notify">sources</envar>: A list of + sources of changesets to consider. This lets you limit + <literal role="hg-ext">notify</literal> to only sending + out email about changes that remote users pushed into + this repository via a server, for example. See + <xref linkend="sec:hook:sources"/> for the sources you + can specify here. + </para> + </listitem></itemizedlist> + + <para id="x_275">If you set the <envar role="rc-item-web">baseurl</envar> + item in the <literal role="rc-web">web</literal> section, + you can use it in a template; it will be available as + <literal>webroot</literal>. + </para> + + <para id="x_276">Here is an example set of <literal + role="hg-ext">notify</literal> configuration information. + </para> + + &ch10-notify-config.lst; + + <para id="x_277">This will produce a message that looks like the + following: + </para> + + &ch10-notify-config-mail.lst; + + </sect3> + <sect3> + <title>Testing and troubleshooting</title> + + <para id="x_278">Do not forget that by default, the <literal + role="hg-ext">notify</literal> extension <emphasis>will not + send any mail</emphasis> until you explicitly configure it to do so, + by setting <envar role="rc-item-notify">test</envar> to + <literal>false</literal>. Until you do that, it simply + prints the message it <emphasis>would</emphasis> send. + </para> + + </sect3> + </sect2> + </sect1> + <sect1 id="sec:hook:ref"> + <title>Information for writers of hooks</title> + + <sect2> + <title>In-process hook execution</title> + + <para id="x_279">An in-process hook is called with arguments of the + following form: + </para> + <programlisting>def myhook(ui, repo, **kwargs): pass</programlisting> + <para id="x_27a">The <literal>ui</literal> parameter is a <literal + role="py-mod-mercurial.ui">ui</literal> object. The + <literal>repo</literal> parameter is a <literal + role="py-mod-mercurial.localrepo">localrepository</literal> + object. The names and values of the + <literal>**kwargs</literal> parameters depend on the hook + being invoked, with the following common features: + </para> + <itemizedlist> + <listitem><para id="x_27b">If a parameter is named + <literal>node</literal> or <literal>parentN</literal>, it + will contain a hexadecimal changeset ID. The empty string + is used to represent <quote>null changeset ID</quote> + instead of a string of zeroes. + </para> + </listitem> + <listitem><para id="x_27c">If a parameter is named + <literal>url</literal>, it will contain the URL of a + remote repository, if that can be determined. + </para> + </listitem> + <listitem><para id="x_27d">Boolean-valued parameters are represented as + Python <literal>bool</literal> objects. + </para> + </listitem></itemizedlist> + + <para id="x_27e">An in-process hook is called without a change to the + process's working directory (unlike external hooks, which are + run in the root of the repository). It must not change the + process's working directory, or it will cause any calls it + makes into the Mercurial API to fail. + </para> + + <para id="x_27f">If a hook returns a boolean <quote>false</quote> value, it + is considered to have succeeded. If it returns a boolean + <quote>true</quote> value or raises an exception, it is + considered to have failed. A useful way to think of the + calling convention is <quote>tell me if you fail</quote>. + </para> + + <para id="x_280">Note that changeset IDs are passed into Python hooks as + hexadecimal strings, not the binary hashes that Mercurial's + APIs normally use. To convert a hash from hex to binary, use + the <literal>bin</literal> function. + </para> + </sect2> + + <sect2> + <title>External hook execution</title> + + <para id="x_281">An external hook is passed to the shell of the user + running Mercurial. Features of that shell, such as variable + substitution and command redirection, are available. The hook + is run in the root directory of the repository (unlike + in-process hooks, which are run in the same directory that + Mercurial was run in). + </para> + + <para id="x_282">Hook parameters are passed to the hook as environment + variables. Each environment variable's name is converted in + upper case and prefixed with the string + <quote><literal>HG_</literal></quote>. For example, if the + name of a parameter is <quote><literal>node</literal></quote>, + the name of the environment variable representing that + parameter will be <quote><literal>HG_NODE</literal></quote>. + </para> + + <para id="x_283">A boolean parameter is represented as the string + <quote><literal>1</literal></quote> for <quote>true</quote>, + <quote><literal>0</literal></quote> for <quote>false</quote>. + If an environment variable is named <envar>HG_NODE</envar>, + <envar>HG_PARENT1</envar> or <envar>HG_PARENT2</envar>, it + contains a changeset ID represented as a hexadecimal string. + The empty string is used to represent <quote>null changeset + ID</quote> instead of a string of zeroes. If an environment + variable is named <envar>HG_URL</envar>, it will contain the + URL of a remote repository, if that can be determined. + </para> + + <para id="x_284">If a hook exits with a status of zero, it is considered to + have succeeded. If it exits with a non-zero status, it is + considered to have failed. + </para> + </sect2> + + <sect2> + <title>Finding out where changesets come from</title> + + <para id="x_285">A hook that involves the transfer of changesets between a + local repository and another may be able to find out + information about the <quote>far side</quote>. Mercurial + knows <emphasis>how</emphasis> changes are being transferred, + and in many cases <emphasis>where</emphasis> they are being + transferred to or from. + </para> + + <sect3 id="sec:hook:sources"> + <title>Sources of changesets</title> + + <para id="x_286">Mercurial will tell a hook what means are, or were, used + to transfer changesets between repositories. This is + provided by Mercurial in a Python parameter named + <literal>source</literal>, or an environment variable named + <envar>HG_SOURCE</envar>. + </para> + + <itemizedlist> + <listitem><para id="x_287"><literal>serve</literal>: Changesets are + transferred to or from a remote repository over http or + ssh. + </para> + </listitem> + <listitem><para id="x_288"><literal>pull</literal>: Changesets are + being transferred via a pull from one repository into + another. + </para> + </listitem> + <listitem><para id="x_289"><literal>push</literal>: Changesets are + being transferred via a push from one repository into + another. + </para> + </listitem> + <listitem><para id="x_28a"><literal>bundle</literal>: Changesets are + being transferred to or from a bundle. + </para> + </listitem></itemizedlist> + </sect3> + + <sect3 id="sec:hook:url"> + <title>Where changes are going&emdash;remote repository + URLs</title> + + <para id="x_28b">When possible, Mercurial will tell a hook the location + of the <quote>far side</quote> of an activity that transfers + changeset data between repositories. This is provided by + Mercurial in a Python parameter named + <literal>url</literal>, or an environment variable named + <envar>HG_URL</envar>. + </para> + + <para id="x_28c">This information is not always known. If a hook is + invoked in a repository that is being served via http or + ssh, Mercurial cannot tell where the remote repository is, + but it may know where the client is connecting from. In + such cases, the URL will take one of the following forms: + </para> + <itemizedlist> + <listitem><para id="x_28d"><literal>remote:ssh:1.2.3.4</literal>&emdash;remote + ssh client, at the IP address + <literal>1.2.3.4</literal>. + </para> + </listitem> + <listitem><para id="x_28e"><literal>remote:http:1.2.3.4</literal>&emdash;remote + http client, at the IP address + <literal>1.2.3.4</literal>. If the client is using SSL, + this will be of the form + <literal>remote:https:1.2.3.4</literal>. + </para> + </listitem> + <listitem><para id="x_28f">Empty&emdash;no information could be + discovered about the remote client. + </para> + </listitem></itemizedlist> + </sect3> + </sect2> + </sect1> + <sect1> + <title>Hook reference</title> + + <sect2 id="sec:hook:changegroup"> + <title><literal role="hook">changegroup</literal>&emdash;after + remote changesets added</title> + + <para id="x_290">This hook is run after a group of pre-existing changesets + has been added to the repository, for example via a <command + role="hg-cmd">hg pull</command> or <command role="hg-cmd">hg + unbundle</command>. This hook is run once per operation + that added one or more changesets. This is in contrast to the + <literal role="hook">incoming</literal> hook, which is run + once per changeset, regardless of whether the changesets + arrive in a group. + </para> + + <para id="x_291">Some possible uses for this hook include kicking off an + automated build or test of the added changesets, updating a + bug database, or notifying subscribers that a repository + contains new changes. + </para> + + <para id="x_292">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_293"><literal>node</literal>: A changeset ID. The + changeset ID of the first changeset in the group that was + added. All changesets between this and + <literal role="tag">tip</literal>, inclusive, were added by a single + <command role="hg-cmd">hg pull</command>, <command + role="hg-cmd">hg push</command> or <command + role="hg-cmd">hg unbundle</command>. + </para> + </listitem> + <listitem><para id="x_294"><literal>source</literal>: A + string. The source of these changes. See <xref + linkend="sec:hook:sources"/> for details. + </para> + </listitem> + <listitem><para id="x_295"><literal>url</literal>: A URL. The + location of the remote repository, if known. See <xref + linkend="sec:hook:url"/> for more information. + </para> + </listitem></itemizedlist> + + <para id="x_296">See also: <literal + role="hook">incoming</literal> (<xref + linkend="sec:hook:incoming"/>), <literal + role="hook">prechangegroup</literal> (<xref + linkend="sec:hook:prechangegroup"/>), <literal + role="hook">pretxnchangegroup</literal> (<xref + linkend="sec:hook:pretxnchangegroup"/>) + </para> + </sect2> + + <sect2 id="sec:hook:commit"> + <title><literal role="hook">commit</literal>&emdash;after a new + changeset is created</title> + + <para id="x_297">This hook is run after a new changeset has been created. + </para> + + <para id="x_298">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_299"><literal>node</literal>: A changeset ID. The + changeset ID of the newly committed changeset. + </para> + </listitem> + <listitem><para id="x_29a"><literal>parent1</literal>: A changeset ID. + The changeset ID of the first parent of the newly + committed changeset. + </para> + </listitem> + <listitem><para id="x_29b"><literal>parent2</literal>: A changeset ID. + The changeset ID of the second parent of the newly + committed changeset. + </para> + </listitem></itemizedlist> + + <para id="x_29c">See also: <literal + role="hook">precommit</literal> (<xref + linkend="sec:hook:precommit"/>), <literal + role="hook">pretxncommit</literal> (<xref + linkend="sec:hook:pretxncommit"/>) + </para> + </sect2> + + <sect2 id="sec:hook:incoming"> + <title><literal role="hook">incoming</literal>&emdash;after one + remote changeset is added</title> + + <para id="x_29d">This hook is run after a pre-existing changeset has been + added to the repository, for example via a <command + role="hg-cmd">hg push</command>. If a group of changesets + was added in a single operation, this hook is called once for + each added changeset. + </para> + + <para id="x_29e">You can use this hook for the same purposes as + the <literal role="hook">changegroup</literal> hook (<xref + linkend="sec:hook:changegroup"/>); it's simply more + convenient sometimes to run a hook once per group of + changesets, while other times it's handier once per changeset. + </para> + + <para id="x_29f">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2a0"><literal>node</literal>: A changeset ID. The + ID of the newly added changeset. + </para> + </listitem> + <listitem><para id="x_2a1"><literal>source</literal>: A + string. The source of these changes. See <xref + linkend="sec:hook:sources"/> for details. + </para> + </listitem> + <listitem><para id="x_2a2"><literal>url</literal>: A URL. The + location of the remote repository, if known. See <xref + linkend="sec:hook:url"/> for more information. + </para> + </listitem></itemizedlist> + + <para id="x_2a3">See also: <literal + role="hook">changegroup</literal> (<xref + linkend="sec:hook:changegroup"/>) <literal + role="hook">prechangegroup</literal> (<xref + linkend="sec:hook:prechangegroup"/>), <literal + role="hook">pretxnchangegroup</literal> (<xref + linkend="sec:hook:pretxnchangegroup"/>) + </para> + </sect2> + + <sect2 id="sec:hook:outgoing"> + <title><literal role="hook">outgoing</literal>&emdash;after + changesets are propagated</title> + + <para id="x_2a4">This hook is run after a group of changesets has been + propagated out of this repository, for example by a <command + role="hg-cmd">hg push</command> or <command role="hg-cmd">hg + bundle</command> command. + </para> + + <para id="x_2a5">One possible use for this hook is to notify administrators + that changes have been pulled. + </para> + + <para id="x_2a6">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2a7"><literal>node</literal>: A changeset ID. The + changeset ID of the first changeset of the group that was + sent. + </para> + </listitem> + <listitem><para id="x_2a8"><literal>source</literal>: A string. The + source of the of the operation (see <xref + linkend="sec:hook:sources"/>). If a remote + client pulled changes from this repository, + <literal>source</literal> will be + <literal>serve</literal>. If the client that obtained + changes from this repository was local, + <literal>source</literal> will be + <literal>bundle</literal>, <literal>pull</literal>, or + <literal>push</literal>, depending on the operation the + client performed. + </para> + </listitem> + <listitem><para id="x_2a9"><literal>url</literal>: A URL. The + location of the remote repository, if known. See <xref + linkend="sec:hook:url"/> for more information. + </para> + </listitem></itemizedlist> + + <para id="x_2aa">See also: <literal + role="hook">preoutgoing</literal> (<xref + linkend="sec:hook:preoutgoing"/>) + </para> + </sect2> + + <sect2 id="sec:hook:prechangegroup"> + <title><literal + role="hook">prechangegroup</literal>&emdash;before starting + to add remote changesets</title> + + <para id="x_2ab">This controlling hook is run before Mercurial begins to + add a group of changesets from another repository. + </para> + + <para id="x_2ac">This hook does not have any information about the + changesets to be added, because it is run before transmission + of those changesets is allowed to begin. If this hook fails, + the changesets will not be transmitted. + </para> + + <para id="x_2ad">One use for this hook is to prevent external changes from + being added to a repository. For example, you could use this + to <quote>freeze</quote> a server-hosted branch temporarily or + permanently so that users cannot push to it, while still + allowing a local administrator to modify the repository. + </para> + + <para id="x_2ae">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2af"><literal>source</literal>: A string. The + source of these changes. See <xref + linkend="sec:hook:sources"/> for details. + </para> + </listitem> + <listitem><para id="x_2b0"><literal>url</literal>: A URL. The + location of the remote repository, if known. See <xref + linkend="sec:hook:url"/> for more information. + </para> + </listitem></itemizedlist> + + <para id="x_2b1">See also: <literal + role="hook">changegroup</literal> (<xref + linkend="sec:hook:changegroup"/>), <literal + role="hook">incoming</literal> (<xref + linkend="sec:hook:incoming"/>), <literal + role="hook">pretxnchangegroup</literal> (<xref + linkend="sec:hook:pretxnchangegroup"/>) + </para> + </sect2> + + <sect2 id="sec:hook:precommit"> + <title><literal role="hook">precommit</literal>&emdash;before + starting to commit a changeset</title> + + <para id="x_2b2">This hook is run before Mercurial begins to commit a new + changeset. It is run before Mercurial has any of the metadata + for the commit, such as the files to be committed, the commit + message, or the commit date. + </para> + + <para id="x_2b3">One use for this hook is to disable the ability to commit + new changesets, while still allowing incoming changesets. + Another is to run a build or test, and only allow the commit + to begin if the build or test succeeds. + </para> + + <para id="x_2b4">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2b5"><literal>parent1</literal>: A changeset ID. + The changeset ID of the first parent of the working + directory. + </para> + </listitem> + <listitem><para id="x_2b6"><literal>parent2</literal>: A changeset ID. + The changeset ID of the second parent of the working + directory. + </para> + </listitem></itemizedlist> + <para id="x_2b7">If the commit proceeds, the parents of the working + directory will become the parents of the new changeset. + </para> + + <para id="x_2b8">See also: <literal role="hook">commit</literal> + (<xref linkend="sec:hook:commit"/>), <literal + role="hook">pretxncommit</literal> (<xref + linkend="sec:hook:pretxncommit"/>) + </para> + </sect2> + + <sect2 id="sec:hook:preoutgoing"> + <title><literal role="hook">preoutgoing</literal>&emdash;before + starting to propagate changesets</title> + + <para id="x_2b9">This hook is invoked before Mercurial knows the identities + of the changesets to be transmitted. + </para> + + <para id="x_2ba">One use for this hook is to prevent changes from being + transmitted to another repository. + </para> + + <para id="x_2bb">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2bc"><literal>source</literal>: A + string. The source of the operation that is attempting to + obtain changes from this repository (see <xref + linkend="sec:hook:sources"/>). See the documentation + for the <literal>source</literal> parameter to the + <literal role="hook">outgoing</literal> hook, in + <xref linkend="sec:hook:outgoing"/>, for possible values + of this parameter. + </para> + </listitem> + <listitem><para id="x_2bd"><literal>url</literal>: A URL. The + location of the remote repository, if known. See <xref + linkend="sec:hook:url"/> for more information. + </para> + </listitem></itemizedlist> + + <para id="x_2be">See also: <literal + role="hook">outgoing</literal> (<xref + linkend="sec:hook:outgoing"/>) + </para> + </sect2> + + <sect2 id="sec:hook:pretag"> + <title><literal role="hook">pretag</literal>&emdash;before + tagging a changeset</title> + + <para id="x_2bf">This controlling hook is run before a tag is created. If + the hook succeeds, creation of the tag proceeds. If the hook + fails, the tag is not created. + </para> + + <para id="x_2c0">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2c1"><literal>local</literal>: A boolean. Whether + the tag is local to this repository instance (i.e. stored + in <filename role="special">.hg/localtags</filename>) or + managed by Mercurial (stored in <filename + role="special">.hgtags</filename>). + </para> + </listitem> + <listitem><para id="x_2c2"><literal>node</literal>: A changeset ID. The + ID of the changeset to be tagged. + </para> + </listitem> + <listitem><para id="x_2c3"><literal>tag</literal>: A string. The name of + the tag to be created. + </para> + </listitem></itemizedlist> + + <para id="x_2c4">If the tag to be created is + revision-controlled, the <literal + role="hook">precommit</literal> and <literal + role="hook">pretxncommit</literal> hooks (<xref + linkend="sec:hook:commit"/> and <xref + linkend="sec:hook:pretxncommit"/>) will also be run. + </para> + + <para id="x_2c5">See also: <literal role="hook">tag</literal> + (<xref linkend="sec:hook:tag"/>) + </para> + </sect2> + + <sect2 id="sec:hook:pretxnchangegroup"> + <title><literal + role="hook">pretxnchangegroup</literal>&emdash;before + completing addition of remote changesets</title> + + <para id="x_2c6">This controlling hook is run before a + transaction&emdash;that manages the addition of a group of new + changesets from outside the repository&emdash;completes. If + the hook succeeds, the transaction completes, and all of the + changesets become permanent within this repository. If the + hook fails, the transaction is rolled back, and the data for + the changesets is erased. + </para> + + <para id="x_2c7">This hook can access the metadata associated with the + almost-added changesets, but it should not do anything + permanent with this data. It must also not modify the working + directory. + </para> + + <para id="x_2c8">While this hook is running, if other Mercurial processes + access this repository, they will be able to see the + almost-added changesets as if they are permanent. This may + lead to race conditions if you do not take steps to avoid + them. + </para> + + <para id="x_2c9">This hook can be used to automatically vet a group of + changesets. If the hook fails, all of the changesets are + <quote>rejected</quote> when the transaction rolls back. + </para> + + <para id="x_2ca">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2cb"><literal>node</literal>: A changeset ID. The + changeset ID of the first changeset in the group that was + added. All changesets between this and + <literal role="tag">tip</literal>, + inclusive, were added by a single <command + role="hg-cmd">hg pull</command>, <command + role="hg-cmd">hg push</command> or <command + role="hg-cmd">hg unbundle</command>. + </para> + </listitem> + <listitem><para id="x_2cc"><literal>source</literal>: A + string. The source of these changes. See <xref + linkend="sec:hook:sources"/> for details. + </para> + </listitem> + <listitem><para id="x_2cd"><literal>url</literal>: A URL. The + location of the remote repository, if known. See <xref + linkend="sec:hook:url"/> for more information. + </para> + </listitem></itemizedlist> + + <para id="x_2ce">See also: <literal + role="hook">changegroup</literal> (<xref + linkend="sec:hook:changegroup"/>), <literal + role="hook">incoming</literal> (<xref + linkend="sec:hook:incoming"/>), <literal + role="hook">prechangegroup</literal> (<xref + linkend="sec:hook:prechangegroup"/>) + </para> + </sect2> + + <sect2 id="sec:hook:pretxncommit"> + <title><literal role="hook">pretxncommit</literal>&emdash;before + completing commit of new changeset</title> + + <para id="x_2cf">This controlling hook is run before a + transaction&emdash;that manages a new commit&emdash;completes. + If the hook succeeds, the transaction completes and the + changeset becomes permanent within this repository. If the + hook fails, the transaction is rolled back, and the commit + data is erased. + </para> + + <para id="x_2d0">This hook can access the metadata associated with the + almost-new changeset, but it should not do anything permanent + with this data. It must also not modify the working + directory. + </para> + + <para id="x_2d1">While this hook is running, if other Mercurial processes + access this repository, they will be able to see the + almost-new changeset as if it is permanent. This may lead to + race conditions if you do not take steps to avoid them. + </para> + + <para id="x_2d2">Parameters to this hook:</para> + + <itemizedlist> + <listitem><para id="x_2d3"><literal>node</literal>: A changeset ID. The + changeset ID of the newly committed changeset. + </para> + </listitem> + <listitem><para id="x_2d4"><literal>parent1</literal>: A changeset ID. + The changeset ID of the first parent of the newly + committed changeset. + </para> + </listitem> + <listitem><para id="x_2d5"><literal>parent2</literal>: A changeset ID. + The changeset ID of the second parent of the newly + committed changeset. + </para> + </listitem></itemizedlist> + + <para id="x_2d6">See also: <literal + role="hook">precommit</literal> (<xref + linkend="sec:hook:precommit"/>) + </para> + </sect2> + + <sect2 id="sec:hook:preupdate"> + <title><literal role="hook">preupdate</literal>&emdash;before + updating or merging working directory</title> + + <para id="x_2d7">This controlling hook is run before an update + or merge of the working directory begins. It is run only if + Mercurial's normal pre-update checks determine that the update + or merge can proceed. If the hook succeeds, the update or + merge may proceed; if it fails, the update or merge does not + start. + </para> + + <para id="x_2d8">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2d9"><literal>parent1</literal>: A + changeset ID. The ID of the parent that the working + directory is to be updated to. If the working directory + is being merged, it will not change this parent. + </para> + </listitem> + <listitem><para id="x_2da"><literal>parent2</literal>: A + changeset ID. Only set if the working directory is being + merged. The ID of the revision that the working directory + is being merged with. + </para> + </listitem></itemizedlist> + + <para id="x_2db">See also: <literal role="hook">update</literal> + (<xref linkend="sec:hook:update"/>)</para> + </sect2> + + <sect2 id="sec:hook:tag"> + <title><literal role="hook">tag</literal>&emdash;after tagging a + changeset</title> + + <para id="x_2dc">This hook is run after a tag has been created. + </para> + + <para id="x_2dd">Parameters to this hook: + </para> + <itemizedlist> + <listitem><para id="x_2de"><literal>local</literal>: A boolean. Whether + the new tag is local to this repository instance (i.e. + stored in <filename + role="special">.hg/localtags</filename>) or managed by + Mercurial (stored in <filename + role="special">.hgtags</filename>). + </para> + </listitem> + <listitem><para id="x_2df"><literal>node</literal>: A changeset ID. The + ID of the changeset that was tagged. + </para> + </listitem> + <listitem><para id="x_2e0"><literal>tag</literal>: A string. The name of + the tag that was created. + </para> + </listitem></itemizedlist> + + <para id="x_2e1">If the created tag is revision-controlled, the <literal + role="hook">commit</literal> hook (section <xref + linkend="sec:hook:commit"/>) is run before this hook. + </para> + + <para id="x_2e2">See also: <literal role="hook">pretag</literal> + (<xref linkend="sec:hook:pretag"/>) + </para> + </sect2> + + <sect2 id="sec:hook:update"> + <title><literal role="hook">update</literal>&emdash;after + updating or merging working directory</title> + + <para id="x_2e3">This hook is run after an update or merge of the working + directory completes. Since a merge can fail (if the external + <command>hgmerge</command> command fails to resolve conflicts + in a file), this hook communicates whether the update or merge + completed cleanly. + </para> + + <itemizedlist> + <listitem><para id="x_2e4"><literal>error</literal>: A boolean. + Indicates whether the update or merge completed + successfully. + </para> + </listitem> + <listitem><para id="x_2e5"><literal>parent1</literal>: A changeset ID. + The ID of the parent that the working directory was + updated to. If the working directory was merged, it will + not have changed this parent. + </para> + </listitem> + <listitem><para id="x_2e6"><literal>parent2</literal>: A changeset ID. + Only set if the working directory was merged. The ID of + the revision that the working directory was merged with. + </para> + </listitem></itemizedlist> + + <para id="x_2e7">See also: <literal role="hook">preupdate</literal> + (<xref linkend="sec:hook:preupdate"/>) + </para> + + </sect2> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch11-template.xml --- a/fr/ch11-template.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch11-template.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,689 +1,685 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Customising the output of Mercurial</title> -<para>\label{chap:template}</para> - -<para>Mercurial provides a powerful mechanism to let you control how it -displays information. The mechanism is based on templates. You can -use templates to generate specific output for a single command, or to -customise the entire appearance of the built-in web interface.</para> - -<sect1> -<title>Using precanned output styles</title> -<para>\label{sec:style}</para> - -<para>Packaged with Mercurial are some output styles that you can use -immediately. A style is simply a precanned template that someone -wrote and installed somewhere that Mercurial can find.</para> - -<para>Before we take a look at Mercurial's bundled styles, let's review its -normal output.</para> - -<para><!-- &interaction.template.simple.normal; --></para> - -<para>This is somewhat informative, but it takes up a lot of space&emdash;five -lines of output per changeset. The <literal>compact</literal> style reduces -this to three lines, presented in a sparse manner.</para> - -<para><!-- &interaction.template.simple.compact; --></para> - -<para>The <literal>changelog</literal> style hints at the expressive power of -Mercurial's templating engine. This style attempts to follow the GNU -Project's changelog guidelines<citation>web:changelog</citation>. -</para> - -<para><!-- &interaction.template.simple.changelog; --> -</para> - -<para>You will not be shocked to learn that Mercurial's default output style -is named <literal>default</literal>. -</para> - -<sect2> -<title>Setting a default style</title> - -<para>You can modify the output style that Mercurial will use for every -command by editing your <filename role="special"> /.hgrc</filename>\ file, naming the style you would -prefer to use. -</para> - -<programlisting> -<para> [ui] - style = compact -</para> -</programlisting> - -<para>If you write a style of your own, you can use it by either providing -the path to your style file, or copying your style file into a -location where Mercurial can find it (typically the <literal>templates</literal> -subdirectory of your Mercurial install directory). -</para> - -</sect2> -</sect1> -<sect1> -<title>Commands that support styles and templates</title> - -<para>All of Mercurial's <quote><literal>log</literal>-like</quote> commands let you use styles -and templates: <command role="hg-cmd">hg incoming</command>, <command role="hg-cmd">hg log</command>, <command role="hg-cmd">hg outgoing</command>, and -<command role="hg-cmd">hg tip</command>. -</para> - -<para>As I write this manual, these are so far the only commands that -support styles and templates. Since these are the most important -commands that need customisable output, there has been little pressure -from the Mercurial user community to add style and template support to -other commands. -</para> - -</sect1> -<sect1> -<title>The basics of templating</title> - -<para>At its simplest, a Mercurial template is a piece of text. Some of the -text never changes, while other parts are <emphasis>expanded</emphasis>, or replaced -with new text, when necessary. -</para> - -<para>Before we continue, let's look again at a simple example of -Mercurial's normal output. -</para> - -<para><!-- &interaction.template.simple.normal; --> -</para> - -<para>Now, let's run the same command, but using a template to change its -output. -</para> - -<para><!-- &interaction.template.simple.simplest; --> -</para> - -<para>The example above illustrates the simplest possible template; it's -just a piece of static text, printed once for each changeset. The -<option role="hg-opt-log">--template</option> option to the <command role="hg-cmd">hg log</command> command tells -Mercurial to use the given text as the template when printing each -changeset. -</para> - -<para>Notice that the template string above ends with the text -<quote><literal>\n</literal></quote>. This is an <emphasis>escape sequence</emphasis>, telling Mercurial -to print a newline at the end of each template item. If you omit this -newline, Mercurial will run each piece of output together. See -section <xref linkend="sec:template:escape"/> for more details of escape sequences. -</para> - -<para>A template that prints a fixed string of text all the time isn't very -useful; let's try something a bit more complex. -</para> - -<para><!-- &interaction.template.simple.simplesub; --> -</para> - -<para>As you can see, the string <quote><literal>{desc}</literal></quote> in the template has been -replaced in the output with the description of each changeset. Every -time Mercurial finds text enclosed in curly braces (<quote><literal>{</literal></quote> -and <quote>\texttt{}}</quote>), it will try to replace the braces and text with -the expansion of whatever is inside. To print a literal curly brace, -you must escape it, as described in section <xref linkend="sec:template:escape"/>. -</para> - -</sect1> -<sect1> -<title>Common template keywords</title> -<para>\label{sec:template:keyword} -</para> - -<para>You can start writing simple templates immediately using the keywords -below. -</para> - -<itemizedlist> -<listitem><para><literal role="template-keyword">author</literal>: String. The unmodified author of the changeset. -</para> -</listitem> -<listitem><para><literal role="template-keyword">branches</literal>: String. The name of the branch on which - the changeset was committed. Will be empty if the branch name was - <literal>default</literal>. -</para> -</listitem> -<listitem><para><literal role="template-keyword">date</literal>: Date information. The date when the changeset - was committed. This is <emphasis>not</emphasis> human-readable; you must pass it - through a filter that will render it appropriately. See - section <xref linkend="sec:template:filter"/> for more information on filters. - The date is expressed as a pair of numbers. The first number is a - Unix UTC timestamp (seconds since January 1, 1970); the second is - the offset of the committer's timezone from UTC, in seconds. -</para> -</listitem> -<listitem><para><literal role="template-keyword">desc</literal>: String. The text of the changeset description. -</para> -</listitem> -<listitem><para><literal role="template-keyword">files</literal>: List of strings. All files modified, added, or - removed by this changeset. -</para> -</listitem> -<listitem><para><literal role="template-keyword">file_adds</literal>: List of strings. Files added by this - changeset. -</para> -</listitem> -<listitem><para><literal role="template-keyword">file_dels</literal>: List of strings. Files removed by this - changeset. -</para> -</listitem> -<listitem><para><literal role="template-keyword">node</literal>: String. The changeset identification hash, as a - 40-character hexadecimal string. -</para> -</listitem> -<listitem><para><literal role="template-keyword">parents</literal>: List of strings. The parents of the - changeset. -</para> -</listitem> -<listitem><para><literal role="template-keyword">rev</literal>: Integer. The repository-local changeset revision - number. -</para> -</listitem> -<listitem><para><literal role="template-keyword">tags</literal>: List of strings. Any tags associated with the - changeset. -</para> -</listitem></itemizedlist> - -<para>A few simple experiments will show us what to expect when we use these -keywords; you can see the results in -figure <xref linkend="fig:template:keywords"/>. -</para> - -<informalfigure> -<para> <!-- &interaction.template.simple.keywords; --> - <caption><para>Template keywords in use</para></caption> - \label{fig:template:keywords} -</para> -</informalfigure> - -<para>As we noted above, the date keyword does not produce human-readable -output, so we must treat it specially. This involves using a -<emphasis>filter</emphasis>, about which more in section <xref linkend="sec:template:filter"/>. -</para> - -<para><!-- &interaction.template.simple.datekeyword; --> -</para> - -</sect1> -<sect1> -<title>Escape sequences</title> -<para>\label{sec:template:escape} -</para> - -<para>Mercurial's templating engine recognises the most commonly used escape -sequences in strings. When it sees a backslash (<quote><literal>\</literal></quote>) -character, it looks at the following character and substitutes the two -characters with a single replacement, as described below. -</para> - -<itemizedlist> -<listitem><para><literal>\textbackslash\textbackslash</literal>: Backslash, <quote><literal>\</literal></quote>, - ASCII 134. -</para> -</listitem> -<listitem><para><literal>\textbackslash n</literal>: Newline, ASCII 12. -</para> -</listitem> -<listitem><para><literal>\textbackslash r</literal>: Carriage return, ASCII 15. -</para> -</listitem> -<listitem><para><literal>\textbackslash t</literal>: Tab, ASCII 11. -</para> -</listitem> -<listitem><para><literal>\textbackslash v</literal>: Vertical tab, ASCII 13. -</para> -</listitem> -<listitem><para><literal>\textbackslash {</literal>: Open curly brace, <quote><literal>{</literal></quote>, ASCII 173. -</para> -</listitem> -<listitem><para><literal>\textbackslash }</literal>: Close curly brace, <quote><literal>}</literal></quote>, ASCII 175. -</para> -</listitem></itemizedlist> - -<para>As indicated above, if you want the expansion of a template to contain -a literal <quote><literal>\</literal></quote>, <quote><literal>{</literal></quote>, or <quote><literal>{</literal></quote> character, you -must escape it. -</para> - -</sect1> -<sect1> -<title>Filtering keywords to change their results</title> -<para>\label{sec:template:filter} -</para> - -<para>Some of the results of template expansion are not immediately easy to -use. Mercurial lets you specify an optional chain of <emphasis>filters</emphasis> -to modify the result of expanding a keyword. You have already seen a -common filter, <literal role="template-kw-filt-date">isodate</literal>, in action above, to make a -date readable. -</para> - -<para>Below is a list of the most commonly used filters that Mercurial -supports. While some filters can be applied to any text, others can -only be used in specific circumstances. The name of each filter is -followed first by an indication of where it can be used, then a -description of its effect. -</para> - -<itemizedlist> -<listitem><para><literal role="template-filter">addbreaks</literal>: Any text. Add an XHTML <quote><literal><br/></literal></quote> - tag before the end of every line except the last. For example, - <quote><literal>foo\nbar</literal></quote> becomes <quote><literal>foo<br/>\nbar</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-date">age</literal>: <literal role="template-keyword">date</literal> keyword. Render the - age of the date, relative to the current time. Yields a string like - <quote><literal>10 minutes</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-filter">basename</literal>: Any text, but most useful for the - <literal role="template-keyword">files</literal> keyword and its relatives. Treat the text as a - path, and return the basename. For example, <quote><literal>foo/bar/baz</literal></quote> - becomes <quote><literal>baz</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-date">date</literal>: <literal role="template-keyword">date</literal> keyword. Render a date - in a similar format to the Unix <literal role="template-keyword">date</literal> command, but with - timezone included. Yields a string like - <quote><literal>Mon Sep 04 15:13:13 2006 -0700</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-author">domain</literal>: Any text, but most useful for the - <literal role="template-keyword">author</literal> keyword. Finds the first string that looks like - an email address, and extract just the domain component. For - example, <quote><literal>Bryan O'Sullivan <bos@serpentine.com></literal></quote> becomes - <quote><literal>serpentine.com</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-author">email</literal>: Any text, but most useful for the - <literal role="template-keyword">author</literal> keyword. Extract the first string that looks like - an email address. For example, - <quote><literal>Bryan O'Sullivan <bos@serpentine.com></literal></quote> becomes - <quote><literal>bos@serpentine.com</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-filter">escape</literal>: Any text. Replace the special XML/XHTML - characters <quote><literal>&</literal></quote>, <quote><literal><</literal></quote> and <quote><literal>></literal></quote> with - XML entities. -</para> -</listitem> -<listitem><para><literal role="template-filter">fill68</literal>: Any text. Wrap the text to fit in 68 - columns. This is useful before you pass text through the - <literal role="template-filter">tabindent</literal> filter, and still want it to fit in an - 80-column fixed-font window. -</para> -</listitem> -<listitem><para><literal role="template-filter">fill76</literal>: Any text. Wrap the text to fit in 76 - columns. -</para> -</listitem> -<listitem><para><literal role="template-filter">firstline</literal>: Any text. Yield the first line of text, - without any trailing newlines. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-date">hgdate</literal>: <literal role="template-keyword">date</literal> keyword. Render the - date as a pair of readable numbers. Yields a string like - <quote><literal>1157407993 25200</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-date">isodate</literal>: <literal role="template-keyword">date</literal> keyword. Render the - date as a text string in ISO 8601 format. Yields a string like - <quote><literal>2006-09-04 15:13:13 -0700</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-filter">obfuscate</literal>: Any text, but most useful for the - <literal role="template-keyword">author</literal> keyword. Yield the input text rendered as a - sequence of XML entities. This helps to defeat some particularly - stupid screen-scraping email harvesting spambots. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-author">person</literal>: Any text, but most useful for the - <literal role="template-keyword">author</literal> keyword. Yield the text before an email address. - For example, <quote><literal>Bryan O'Sullivan <bos@serpentine.com></literal></quote> - becomes <quote><literal>Bryan O'Sullivan</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-date">rfc822date</literal>: <literal role="template-keyword">date</literal> keyword. Render a - date using the same format used in email headers. Yields a string - like <quote><literal>Mon, 04 Sep 2006 15:13:13 -0700</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-node">short</literal>: Changeset hash. Yield the short form - of a changeset hash, i.e. a 12-character hexadecimal string. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-date">shortdate</literal>: <literal role="template-keyword">date</literal> keyword. Render - the year, month, and day of the date. Yields a string like - <quote><literal>2006-09-04</literal></quote>. -</para> -</listitem> -<listitem><para><literal role="template-filter">strip</literal>: Any text. Strip all leading and trailing - whitespace from the string. -</para> -</listitem> -<listitem><para><literal role="template-filter">tabindent</literal>: Any text. Yield the text, with every line - except the first starting with a tab character. -</para> -</listitem> -<listitem><para><literal role="template-filter">urlescape</literal>: Any text. Escape all characters that are - considered <quote>special</quote> by URL parsers. For example, <literal>foo bar</literal> - becomes <literal>foo%20bar</literal>. -</para> -</listitem> -<listitem><para><literal role="template-kw-filt-author">user</literal>: Any text, but most useful for the - <literal role="template-keyword">author</literal> keyword. Return the <quote>user</quote> portion of an email - address. For example, - <quote><literal>Bryan O'Sullivan <bos@serpentine.com></literal></quote> becomes - <quote><literal>bos</literal></quote>. -</para> -</listitem></itemizedlist> - -<informalfigure> -<para> <!-- &interaction.template.simple.manyfilters; --> - <caption><para>Template filters in action</para></caption> - \label{fig:template:filters} -</para> -</informalfigure> - -<note> -<para> If you try to apply a filter to a piece of data that it cannot - process, Mercurial will fail and print a Python exception. For - example, trying to run the output of the <literal role="template-keyword">desc</literal> keyword - into the <literal role="template-kw-filt-date">isodate</literal> filter is not a good idea. -</para> -</note> - -<sect2> -<title>Combining filters</title> - -<para>It is easy to combine filters to yield output in the form you would -like. The following chain of filters tidies up a description, then -makes sure that it fits cleanly into 68 columns, then indents it by a -further 8 characters (at least on Unix-like systems, where a tab is -conventionally 8 characters wide). -</para> - -<para><!-- &interaction.template.simple.combine; --> -</para> - -<para>Note the use of <quote><literal>\t</literal></quote> (a tab character) in the template to -force the first line to be indented; this is necessary since -<literal role="template-keyword">tabindent</literal> indents all lines <emphasis>except</emphasis> the first. -</para> - -<para>Keep in mind that the order of filters in a chain is significant. The -first filter is applied to the result of the keyword; the second to -the result of the first filter; and so on. For example, using -<literal>fill68|tabindent</literal> gives very different results from -<literal>tabindent|fill68</literal>. -</para> - - -</sect2> -</sect1> -<sect1> -<title>From templates to styles</title> - -<para>A command line template provides a quick and simple way to format some -output. Templates can become verbose, though, and it's useful to be -able to give a template a name. A style file is a template with a -name, stored in a file. -</para> - -<para>More than that, using a style file unlocks the power of Mercurial's -templating engine in ways that are not possible using the command line -<option role="hg-opt-log">--template</option> option. -</para> - -<sect2> -<title>The simplest of style files</title> - -<para>Our simple style file contains just one line: -</para> - -<para><!-- &interaction.template.simple.rev; --> -</para> - -<para>This tells Mercurial, <quote>if you're printing a changeset, use the text -on the right as the template</quote>. -</para> - -</sect2> -<sect2> -<title>Style file syntax</title> - -<para>The syntax rules for a style file are simple. -</para> - -<itemizedlist> -<listitem><para>The file is processed one line at a time. -</para> -</listitem> -</para> -</listitem> -<listitem><para>Leading and trailing white space are ignored. -</para> -</listitem> -</para> -</listitem> -<listitem><para>Empty lines are skipped. -</para> -</listitem> -</para> -</listitem> -<listitem><para>If a line starts with either of the characters <quote><literal>#</literal></quote> or - <quote><literal>;</literal></quote>, the entire line is treated as a comment, and skipped - as if empty. -</para> -</listitem> -</para> -</listitem> -<listitem><para>A line starts with a keyword. This must start with an - alphabetic character or underscore, and can subsequently contain any - alphanumeric character or underscore. (In regexp notation, a - keyword must match <literal>[A-Za-z_][A-Za-z0-9_]*</literal>.) -</para> -</listitem> -</para> -</listitem> -<listitem><para>The next element must be an <quote><literal>=</literal></quote> character, which can - be preceded or followed by an arbitrary amount of white space. -</para> -</listitem> -</para> -</listitem> -<listitem><para>If the rest of the line starts and ends with matching quote - characters (either single or double quote), it is treated as a - template body. -</para> -</listitem> -</para> -</listitem> -<listitem><para>If the rest of the line <emphasis>does not</emphasis> start with a quote - character, it is treated as the name of a file; the contents of this - file will be read and used as a template body. -</para> -</listitem></itemizedlist> - -</sect2> -</sect1> -<sect1> -<title>Style files by example</title> - -<para>To illustrate how to write a style file, we will construct a few by -example. Rather than provide a complete style file and walk through -it, we'll mirror the usual process of developing a style file by -starting with something very simple, and walking through a series of -successively more complete examples. -</para> - -<sect2> -<title>Identifying mistakes in style files</title> - -<para>If Mercurial encounters a problem in a style file you are working on, -it prints a terse error message that, once you figure out what it -means, is actually quite useful. -</para> - -<para><!-- &interaction.template.svnstyle.syntax.input; --> -</para> - -<para>Notice that <filename>broken.style</filename> attempts to define a -<literal>changeset</literal> keyword, but forgets to give any content for it. -When instructed to use this style file, Mercurial promptly complains. -</para> - -<para><!-- &interaction.template.svnstyle.syntax.error; --> -</para> - -<para>This error message looks intimidating, but it is not too hard to -follow. -</para> - -<itemizedlist> -<listitem><para>The first component is simply Mercurial's way of saying <quote>I am - giving up</quote>. -</para> -</listitem><programlisting> -<listitem><para> <emphasis role="bold">abort:</emphasis> broken.style:1: parse error -</para> -</listitem></programlisting> - -</para> -</listitem> -<listitem><para>Next comes the name of the style file that contains the error. -</para> -</listitem><programlisting> -<listitem><para> abort: <emphasis role="bold">broken.style</emphasis>:1: parse error -</para> -</listitem></programlisting> - -</para> -</listitem> -<listitem><para>Following the file name is the line number where the error was - encountered. -</para> -</listitem><programlisting> -<listitem><para> abort: broken.style:<emphasis role="bold">1</emphasis>: parse error -</para> -</listitem></programlisting> - -</para> -</listitem> -<listitem><para>Finally, a description of what went wrong. -</para> -</listitem><programlisting> -<listitem><para> abort: broken.style:1: <emphasis role="bold">parse error</emphasis> -</para> -</listitem></programlisting> -<listitem><para> The description of the problem is not always clear (as in this - case), but even when it is cryptic, it is almost always trivial to - visually inspect the offending line in the style file and see what - is wrong. -</para> -</listitem></itemizedlist> - -</sect2> -<sect2> -<title>Uniquely identifying a repository</title> - -<para>If you would like to be able to identify a Mercurial repository -<quote>fairly uniquely</quote> using a short string as an identifier, you can -use the first revision in the repository. -<!-- &interaction.template.svnstyle.id; --> -This is not guaranteed to be unique, but it is nevertheless useful in -many cases. -</para> -<itemizedlist> -<listitem><para>It will not work in a completely empty repository, because such - a repository does not have a revision zero. -</para> -</listitem> -<listitem><para>Neither will it work in the (extremely rare) case where a - repository is a merge of two or more formerly independent - repositories, and you still have those repositories around. -</para> -</listitem></itemizedlist> -<para>Here are some uses to which you could put this identifier: -</para> -<itemizedlist> -<listitem><para>As a key into a table for a database that manages repositories - on a server. -</para> -</listitem> -<listitem><para>As half of a {<emphasis>repository ID</emphasis>, <emphasis>revision ID</emphasis>} tuple. - Save this information away when you run an automated build or other - activity, so that you can <quote>replay</quote> the build later if necessary. -</para> -</listitem></itemizedlist> - -</sect2> -<sect2> -<title>Mimicking Subversion's output</title> - -<para>Let's try to emulate the default output format used by another -revision control tool, Subversion. -<!-- &interaction.template.svnstyle.short; --> -</para> - -<para>Since Subversion's output style is fairly simple, it is easy to -copy-and-paste a hunk of its output into a file, and replace the text -produced above by Subversion with the template values we'd like to see -expanded. -<!-- &interaction.template.svnstyle.template; --> -</para> - -<para>There are a few small ways in which this template deviates from the -output produced by Subversion. -</para> -<itemizedlist> -<listitem><para>Subversion prints a <quote>readable</quote> date (the <quote>\texttt{Wed, 27 Sep - 2006}</quote> in the example output above) in parentheses. Mercurial's - templating engine does not provide a way to display a date in this - format without also printing the time and time zone. -</para> -</listitem> -<listitem><para>We emulate Subversion's printing of <quote>separator</quote> lines full of - <quote><literal>-</literal></quote> characters by ending the template with such a line. - We use the templating engine's <literal role="template-keyword">header</literal> keyword to print a - separator line as the first line of output (see below), thus - achieving similar output to Subversion. -</para> -</listitem> -<listitem><para>Subversion's output includes a count in the header of the number - of lines in the commit message. We cannot replicate this in - Mercurial; the templating engine does not currently provide a filter - that counts the number of lines the template generates. -</para> -</listitem></itemizedlist> -<para>It took me no more than a minute or two of work to replace literal -text from an example of Subversion's output with some keywords and -filters to give the template above. The style file simply refers to -the template. -<!-- &interaction.template.svnstyle.style; --> -</para> - -<para>We could have included the text of the template file directly in the -style file by enclosing it in quotes and replacing the newlines with -<quote><literal>\n</literal></quote> sequences, but it would have made the style file too -difficult to read. Readability is a good guide when you're trying to -decide whether some text belongs in a style file, or in a template -file that the style file points to. If the style file will look too -big or cluttered if you insert a literal piece of text, drop it into a -template instead. -</para> - -</sect2> -</sect1> +<chapter id="chap:template"> + <?dbhtml filename="customizing-the-output-of-mercurial.html"?> + <title>Customizing the output of Mercurial</title> + + <para id="x_578">Mercurial provides a powerful mechanism to let you control how + it displays information. The mechanism is based on templates. + You can use templates to generate specific output for a single + command, or to customize the entire appearance of the built-in web + interface.</para> + + <sect1 id="sec:style"> + <title>Using precanned output styles</title> + + <para id="x_579">Packaged with Mercurial are some output styles that you can + use immediately. A style is simply a precanned template that + someone wrote and installed somewhere that Mercurial can + find.</para> + + <para id="x_57a">Before we take a look at Mercurial's bundled styles, let's + review its normal output.</para> + + &interaction.template.simple.normal; + + <para id="x_57b">This is somewhat informative, but it takes up a lot of + space&emdash;five lines of output per changeset. The + <literal>compact</literal> style reduces this to three lines, + presented in a sparse manner.</para> + + &interaction.template.simple.compact; + + <para id="x_57c">The <literal>changelog</literal> style hints at the + expressive power of Mercurial's templating engine. This style + attempts to follow the GNU Project's changelog + guidelines<citation>web:changelog</citation>.</para> + + &interaction.template.simple.changelog; + + <para id="x_57d">You will not be shocked to learn that Mercurial's default + output style is named <literal>default</literal>.</para> + + <sect2> + <title>Setting a default style</title> + + <para id="x_57e">You can modify the output style that Mercurial will use + for every command by editing your <filename + role="special">~/.hgrc</filename> file, naming the style + you would prefer to use.</para> + + <programlisting>[ui] +style = compact</programlisting> + + <para id="x_57f">If you write a style of your own, you can use it by either + providing the path to your style file, or copying your style + file into a location where Mercurial can find it (typically + the <literal>templates</literal> subdirectory of your + Mercurial install directory).</para> + </sect2> + </sect1> + + <sect1> + <title>Commands that support styles and templates</title> + + <para id="x_580">All of Mercurial's + <quote><literal>log</literal>-like</quote> commands let you use + styles and templates: <command role="hg-cmd">hg + incoming</command>, <command role="hg-cmd">hg log</command>, + <command role="hg-cmd">hg outgoing</command>, and <command + role="hg-cmd">hg tip</command>.</para> + + <para id="x_581">As I write this manual, these are so far the only commands + that support styles and templates. Since these are the most + important commands that need customizable output, there has been + little pressure from the Mercurial user community to add style + and template support to other commands.</para> + </sect1> + + <sect1> + <title>The basics of templating</title> + + <para id="x_582">At its simplest, a Mercurial template is a piece of text. + Some of the text never changes, while other parts are + <emphasis>expanded</emphasis>, or replaced with new text, when + necessary.</para> + + <para id="x_583">Before we continue, let's look again at a simple example of + Mercurial's normal output.</para> + + &interaction.template.simple.normal; + + <para id="x_584">Now, let's run the same command, but using a template to + change its output.</para> + + &interaction.template.simple.simplest; + + <para id="x_585">The example above illustrates the simplest possible + template; it's just a piece of static text, printed once for + each changeset. The <option + role="hg-opt-log">--template</option> option to the <command + role="hg-cmd">hg log</command> command tells Mercurial to use + the given text as the template when printing each + changeset.</para> + + <para id="x_586">Notice that the template string above ends with the text + <quote><literal>\n</literal></quote>. This is an + <emphasis>escape sequence</emphasis>, telling Mercurial to print + a newline at the end of each template item. If you omit this + newline, Mercurial will run each piece of output together. See + <xref linkend="sec:template:escape"/> for more details + of escape sequences.</para> + + <para id="x_587">A template that prints a fixed string of text all the time + isn't very useful; let's try something a bit more + complex.</para> + + &interaction.template.simple.simplesub; + + <para id="x_588">As you can see, the string + <quote><literal>{desc}</literal></quote> in the template has + been replaced in the output with the description of each + changeset. Every time Mercurial finds text enclosed in curly + braces (<quote><literal>{</literal></quote> and + <quote><literal>}</literal></quote>), it will try to replace the + braces and text with the expansion of whatever is inside. To + print a literal curly brace, you must escape it, as described in + <xref linkend="sec:template:escape"/>.</para> + </sect1> + + <sect1 id="sec:template:keyword"> + <title>Common template keywords</title> + + <para id="x_589">You can start writing simple templates immediately using the + keywords below.</para> + + <itemizedlist> + <listitem><para id="x_58a"><literal + role="template-keyword">author</literal>: String. The + unmodified author of the changeset.</para> + </listitem> + <listitem><para id="x_58b"><literal + role="template-keyword">branches</literal>: String. The + name of the branch on which the changeset was committed. + Will be empty if the branch name was + <literal>default</literal>.</para> + </listitem> + <listitem><para id="x_58c"><literal role="template-keyword">date</literal>: + Date information. The date when the changeset was + committed. This is <emphasis>not</emphasis> human-readable; + you must pass it through a filter that will render it + appropriately. See <xref + linkend="sec:template:filter"/> for more information + on filters. The date is expressed as a pair of numbers. The + first number is a Unix UTC timestamp (seconds since January + 1, 1970); the second is the offset of the committer's + timezone from UTC, in seconds.</para> + </listitem> + <listitem><para id="x_58d"><literal role="template-keyword">desc</literal>: + String. The text of the changeset description.</para> + </listitem> + <listitem><para id="x_58e"><literal + role="template-keyword">files</literal>: List of strings. + All files modified, added, or removed by this + changeset.</para> + </listitem> + <listitem><para id="x_58f"><literal + role="template-keyword">file_adds</literal>: List of + strings. Files added by this changeset.</para> + </listitem> + <listitem><para id="x_590"><literal + role="template-keyword">file_dels</literal>: List of + strings. Files removed by this changeset.</para> + </listitem> + <listitem><para id="x_591"><literal role="template-keyword">node</literal>: + String. The changeset identification hash, as a + 40-character hexadecimal string.</para> + </listitem> + <listitem><para id="x_592"><literal + role="template-keyword">parents</literal>: List of + strings. The parents of the changeset.</para> + </listitem> + <listitem><para id="x_593"><literal role="template-keyword">rev</literal>: + Integer. The repository-local changeset revision + number.</para> + </listitem> + <listitem><para id="x_594"><literal role="template-keyword">tags</literal>: + List of strings. Any tags associated with the + changeset.</para> + </listitem> + </itemizedlist> + + <para id="x_595">A few simple experiments will show us what to expect when we + use these keywords; you can see the results below.</para> + + &interaction.template.simple.keywords; + + <para id="x_596">As we noted above, the date keyword does not produce + human-readable output, so we must treat it specially. This + involves using a <emphasis>filter</emphasis>, about which more + in <xref linkend="sec:template:filter"/>.</para> + + &interaction.template.simple.datekeyword; + </sect1> + + <sect1 id="sec:template:escape"> + <title>Escape sequences</title> + + <para id="x_597">Mercurial's templating engine recognises the most commonly + used escape sequences in strings. When it sees a backslash + (<quote><literal>\</literal></quote>) character, it looks at the + following character and substitutes the two characters with a + single replacement, as described below.</para> + + <itemizedlist> + <listitem><para id="x_598"><literal>\</literal>: + Backslash, <quote><literal>\</literal></quote>, ASCII + 134.</para> + </listitem> + <listitem><para id="x_599"><literal>\n</literal>: Newline, + ASCII 12.</para> + </listitem> + <listitem><para id="x_59a"><literal>\r</literal>: Carriage + return, ASCII 15.</para> + </listitem> + <listitem><para id="x_59b"><literal>\t</literal>: Tab, ASCII + 11.</para> + </listitem> + <listitem><para id="x_59c"><literal>\v</literal>: Vertical + tab, ASCII 13.</para> + </listitem> + <listitem><para id="x_59d"><literal>\{</literal>: Open curly + brace, <quote><literal>{</literal></quote>, ASCII + 173.</para> + </listitem> + <listitem><para id="x_59e"><literal>\}</literal>: Close curly + brace, <quote><literal>}</literal></quote>, ASCII + 175.</para> + </listitem></itemizedlist> + + <para id="x_59f">As indicated above, if you want the expansion of a template + to contain a literal <quote><literal>\</literal></quote>, + <quote><literal>{</literal></quote>, or + <quote><literal>{</literal></quote> character, you must escape + it.</para> + </sect1> + + <sect1 id="sec:template:filter"> + <title>Filtering keywords to change their results</title> + + <para id="x_5a0">Some of the results of template expansion are not + immediately easy to use. Mercurial lets you specify an optional + chain of <emphasis>filters</emphasis> to modify the result of + expanding a keyword. You have already seen a common filter, + <literal role="template-kw-filt-date">isodate</literal>, in + action above, to make a date readable.</para> + + <para id="x_5a1">Below is a list of the most commonly used filters that + Mercurial supports. While some filters can be applied to any + text, others can only be used in specific circumstances. The + name of each filter is followed first by an indication of where + it can be used, then a description of its effect.</para> + + <itemizedlist> + <listitem><para id="x_5a2"><literal + role="template-filter">addbreaks</literal>: Any text. Add + an XHTML <quote><literal><br/></literal></quote> tag + before the end of every line except the last. For example, + <quote><literal>foo\nbar</literal></quote> becomes + <quote><literal>foo<br/>\nbar</literal></quote>.</para> + </listitem> + <listitem><para id="x_5a3"><literal + role="template-kw-filt-date">age</literal>: <literal + role="template-keyword">date</literal> keyword. Render + the age of the date, relative to the current time. Yields a + string like <quote><literal>10 + minutes</literal></quote>.</para> + </listitem> + <listitem><para id="x_5a4"><literal + role="template-filter">basename</literal>: Any text, but + most useful for the <literal + role="template-keyword">files</literal> keyword and its + relatives. Treat the text as a path, and return the + basename. For example, + <quote><literal>foo/bar/baz</literal></quote> becomes + <quote><literal>baz</literal></quote>.</para> + </listitem> + <listitem><para id="x_5a5"><literal + role="template-kw-filt-date">date</literal>: <literal + role="template-keyword">date</literal> keyword. Render a + date in a similar format to the Unix <literal + role="template-keyword">date</literal> command, but with + timezone included. Yields a string like <quote><literal>Mon + Sep 04 15:13:13 2006 -0700</literal></quote>.</para> + </listitem> + <listitem><para id="x_5a6"><literal + role="template-kw-filt-author">domain</literal>: Any text, + but most useful for the <literal + role="template-keyword">author</literal> keyword. Finds + the first string that looks like an email address, and + extract just the domain component. For example, + <quote><literal>Bryan O'Sullivan + <bos@serpentine.com></literal></quote> becomes + <quote><literal>serpentine.com</literal></quote>.</para> + </listitem> + <listitem><para id="x_5a7"><literal + role="template-kw-filt-author">email</literal>: Any text, + but most useful for the <literal + role="template-keyword">author</literal> keyword. Extract + the first string that looks like an email address. For + example, <quote><literal>Bryan O'Sullivan + <bos@serpentine.com></literal></quote> becomes + <quote><literal>bos@serpentine.com</literal></quote>.</para> + </listitem> + <listitem><para id="x_5a8"><literal + role="template-filter">escape</literal>: Any text. + Replace the special XML/XHTML characters + <quote><literal>&</literal></quote>, + <quote><literal><</literal></quote> and + <quote><literal>></literal></quote> with XML + entities.</para> + </listitem> + <listitem><para id="x_5a9"><literal + role="template-filter">fill68</literal>: Any text. Wrap + the text to fit in 68 columns. This is useful before you + pass text through the <literal + role="template-filter">tabindent</literal> filter, and + still want it to fit in an 80-column fixed-font + window.</para> + </listitem> + <listitem><para id="x_5aa"><literal + role="template-filter">fill76</literal>: Any text. Wrap + the text to fit in 76 columns.</para> + </listitem> + <listitem><para id="x_5ab"><literal + role="template-filter">firstline</literal>: Any text. + Yield the first line of text, without any trailing + newlines.</para> + </listitem> + <listitem><para id="x_5ac"><literal + role="template-kw-filt-date">hgdate</literal>: <literal + role="template-keyword">date</literal> keyword. Render + the date as a pair of readable numbers. Yields a string + like <quote><literal>1157407993 + 25200</literal></quote>.</para> + </listitem> + <listitem><para id="x_5ad"><literal + role="template-kw-filt-date">isodate</literal>: <literal + role="template-keyword">date</literal> keyword. Render + the date as a text string in ISO 8601 format. Yields a + string like <quote><literal>2006-09-04 15:13:13 + -0700</literal></quote>.</para> + </listitem> + <listitem><para id="x_5ae"><literal + role="template-filter">obfuscate</literal>: Any text, but + most useful for the <literal + role="template-keyword">author</literal> keyword. Yield + the input text rendered as a sequence of XML entities. This + helps to defeat some particularly stupid screen-scraping + email harvesting spambots.</para> + </listitem> + <listitem><para id="x_5af"><literal + role="template-kw-filt-author">person</literal>: Any text, + but most useful for the <literal + role="template-keyword">author</literal> keyword. Yield + the text before an email address. For example, + <quote><literal>Bryan O'Sullivan + <bos@serpentine.com></literal></quote> becomes + <quote><literal>Bryan O'Sullivan</literal></quote>.</para> + </listitem> + <listitem><para id="x_5b0"><literal + role="template-kw-filt-date">rfc822date</literal>: + <literal role="template-keyword">date</literal> keyword. + Render a date using the same format used in email headers. + Yields a string like <quote><literal>Mon, 04 Sep 2006 + 15:13:13 -0700</literal></quote>.</para> + </listitem> + <listitem><para id="x_5b1"><literal + role="template-kw-filt-node">short</literal>: Changeset + hash. Yield the short form of a changeset hash, i.e. a + 12-character hexadecimal string.</para> + </listitem> + <listitem><para id="x_5b2"><literal + role="template-kw-filt-date">shortdate</literal>: <literal + role="template-keyword">date</literal> keyword. Render + the year, month, and day of the date. Yields a string like + <quote><literal>2006-09-04</literal></quote>.</para> + </listitem> + <listitem><para id="x_5b3"><literal role="template-filter">strip</literal>: + Any text. Strip all leading and trailing whitespace from + the string.</para> + </listitem> + <listitem><para id="x_5b4"><literal + role="template-filter">tabindent</literal>: Any text. + Yield the text, with every line except the first starting + with a tab character.</para> + </listitem> + <listitem><para id="x_5b5"><literal + role="template-filter">urlescape</literal>: Any text. + Escape all characters that are considered + <quote>special</quote> by URL parsers. For example, + <literal>foo bar</literal> becomes + <literal>foo%20bar</literal>.</para> + </listitem> + <listitem><para id="x_5b6"><literal + role="template-kw-filt-author">user</literal>: Any text, + but most useful for the <literal + role="template-keyword">author</literal> keyword. Return + the <quote>user</quote> portion of an email address. For + example, <quote><literal>Bryan O'Sullivan + <bos@serpentine.com></literal></quote> becomes + <quote><literal>bos</literal></quote>.</para> + </listitem> + </itemizedlist> + + &interaction.template.simple.manyfilters; + + <note> + <para id="x_5b7"> If you try to apply a filter to a piece of data that it + cannot process, Mercurial will fail and print a Python + exception. For example, trying to run the output of the + <literal role="template-keyword">desc</literal> keyword into + the <literal role="template-kw-filt-date">isodate</literal> + filter is not a good idea.</para> + </note> + + <sect2> + <title>Combining filters</title> + + <para id="x_5b8">It is easy to combine filters to yield output in the form + you would like. The following chain of filters tidies up a + description, then makes sure that it fits cleanly into 68 + columns, then indents it by a further 8 characters (at least + on Unix-like systems, where a tab is conventionally 8 + characters wide).</para> + + &interaction.template.simple.combine; + + <para id="x_5b9">Note the use of <quote><literal>\t</literal></quote> (a + tab character) in the template to force the first line to be + indented; this is necessary since <literal + role="template-keyword">tabindent</literal> indents all + lines <emphasis>except</emphasis> the first.</para> + + <para id="x_5ba">Keep in mind that the order of filters in a chain is + significant. The first filter is applied to the result of the + keyword; the second to the result of the first filter; and so + on. For example, using <literal>fill68|tabindent</literal> + gives very different results from + <literal>tabindent|fill68</literal>.</para> + </sect2> + </sect1> + + <sect1> + <title>From templates to styles</title> + + <para id="x_5bb">A command line template provides a quick and simple way to + format some output. Templates can become verbose, though, and + it's useful to be able to give a template a name. A style file + is a template with a name, stored in a file.</para> + + <para id="x_5bc">More than that, using a style file unlocks the power of + Mercurial's templating engine in ways that are not possible + using the command line <option + role="hg-opt-log">--template</option> option.</para> + + <sect2> + <title>The simplest of style files</title> + + <para id="x_5bd">Our simple style file contains just one line:</para> + + &interaction.template.simple.rev; + + <para id="x_5be">This tells Mercurial, <quote>if you're printing a + changeset, use the text on the right as the + template</quote>.</para> + </sect2> + + <sect2> + <title>Style file syntax</title> + + <para id="x_5bf">The syntax rules for a style file are simple.</para> + + <itemizedlist> + <listitem><para id="x_5c0">The file is processed one line at a + time.</para> + </listitem> + <listitem><para id="x_5c1">Leading and trailing white space are + ignored.</para> + </listitem> + <listitem><para id="x_5c2">Empty lines are skipped.</para> + </listitem> + <listitem><para id="x_5c3">If a line starts with either of the characters + <quote><literal>#</literal></quote> or + <quote><literal>;</literal></quote>, the entire line is + treated as a comment, and skipped as if empty.</para> + </listitem> + <listitem><para id="x_5c4">A line starts with a keyword. This must start + with an alphabetic character or underscore, and can + subsequently contain any alphanumeric character or + underscore. (In regexp notation, a keyword must match + <literal>[A-Za-z_][A-Za-z0-9_]*</literal>.)</para> + </listitem> + <listitem><para id="x_5c5">The next element must be an + <quote><literal>=</literal></quote> character, which can + be preceded or followed by an arbitrary amount of white + space.</para> + </listitem> + <listitem><para id="x_5c6">If the rest of the line starts and ends with + matching quote characters (either single or double quote), + it is treated as a template body.</para> + </listitem> + <listitem><para id="x_5c7">If the rest of the line <emphasis>does + not</emphasis> start with a quote character, it is + treated as the name of a file; the contents of this file + will be read and used as a template body.</para> + </listitem></itemizedlist> + </sect2> + </sect1> + + <sect1> + <title>Style files by example</title> + + <para id="x_5c8">To illustrate how to write a style file, we will construct a + few by example. Rather than provide a complete style file and + walk through it, we'll mirror the usual process of developing a + style file by starting with something very simple, and walking + through a series of successively more complete examples.</para> + + <sect2> + <title>Identifying mistakes in style files</title> + + <para id="x_5c9">If Mercurial encounters a problem in a style file you are + working on, it prints a terse error message that, once you + figure out what it means, is actually quite useful.</para> + +&interaction.template.svnstyle.syntax.input; + + <para id="x_5ca">Notice that <filename>broken.style</filename> attempts to + define a <literal>changeset</literal> keyword, but forgets to + give any content for it. When instructed to use this style + file, Mercurial promptly complains.</para> + + &interaction.template.svnstyle.syntax.error; + + <para id="x_5cb">This error message looks intimidating, but it is not too + hard to follow.</para> + + <itemizedlist> + <listitem><para id="x_5cc">The first component is simply Mercurial's way + of saying <quote>I am giving up</quote>.</para> + <programlisting>___abort___: broken.style:1: parse error</programlisting> + </listitem> + <listitem><para id="x_5cd">Next comes the name of the style file that + contains the error.</para> + <programlisting>abort: ___broken.style___:1: parse error</programlisting> + </listitem> + <listitem><para id="x_5ce">Following the file name is the line number + where the error was encountered.</para> + <programlisting>abort: broken.style:___1___: parse error</programlisting> + </listitem> + <listitem><para id="x_5cf">Finally, a description of what went + wrong.</para> + <programlisting>abort: broken.style:1: ___parse error___</programlisting> + </listitem> + <listitem><para id="x_5d0">The description of the problem is not always + clear (as in this case), but even when it is cryptic, it + is almost always trivial to visually inspect the offending + line in the style file and see what is wrong.</para> + </listitem> + </itemizedlist> + </sect2> + + <sect2> + <title>Uniquely identifying a repository</title> + + <para id="x_5d1">If you would like to be able to identify a Mercurial + repository <quote>fairly uniquely</quote> using a short string + as an identifier, you can use the first revision in the + repository.</para> + + &interaction.template.svnstyle.id; + + <para id="x_5d2">This is likely to be unique, and so it is + useful in many cases. There are a few caveats.</para> + <itemizedlist> + <listitem><para id="x_5d3">It will not work in a completely empty + repository, because such a repository does not have a + revision zero.</para> + </listitem> + <listitem><para id="x_5d4">Neither will it work in the (extremely rare) + case where a repository is a merge of two or more formerly + independent repositories, and you still have those + repositories around.</para> + </listitem></itemizedlist> + <para id="x_5d5">Here are some uses to which you could put this + identifier:</para> + <itemizedlist> + <listitem><para id="x_5d6">As a key into a table for a database that + manages repositories on a server.</para> + </listitem> + <listitem><para id="x_5d7">As half of a {<emphasis>repository + ID</emphasis>, <emphasis>revision ID</emphasis>} tuple. + Save this information away when you run an automated build + or other activity, so that you can <quote>replay</quote> + the build later if necessary.</para> + </listitem> + </itemizedlist> + </sect2> + + <sect2> + <title>Listing files on multiple lines</title> + + <para id="x_714">Suppose we want to list the files changed by a changeset, + one per line, with a little indentation before each file + name.</para> + + &interaction.ch10-multiline.go; + </sect2> + + <sect2> + <title>Mimicking Subversion's output</title> + + <para id="x_5d8">Let's try to emulate the default output format used by + another revision control tool, Subversion.</para> + + &interaction.template.svnstyle.short; + + <para id="x_5d9">Since Subversion's output style is fairly simple, it is + easy to copy-and-paste a hunk of its output into a file, and + replace the text produced above by Subversion with the + template values we'd like to see expanded.</para> + + &interaction.template.svnstyle.template; + + <para id="x_5da">There are a few small ways in which this template deviates + from the output produced by Subversion.</para> + <itemizedlist> + <listitem><para id="x_5db">Subversion prints a <quote>readable</quote> + date (the <quote><literal>Wed, 27 Sep 2006</literal></quote> in the + example output above) in parentheses. Mercurial's + templating engine does not provide a way to display a date + in this format without also printing the time and time + zone.</para> + </listitem> + <listitem><para id="x_5dc">We emulate Subversion's printing of + <quote>separator</quote> lines full of + <quote><literal>-</literal></quote> characters by ending + the template with such a line. We use the templating + engine's <literal role="template-keyword">header</literal> + keyword to print a separator line as the first line of + output (see below), thus achieving similar output to + Subversion.</para> + </listitem> + <listitem><para id="x_5dd">Subversion's output includes a count in the + header of the number of lines in the commit message. We + cannot replicate this in Mercurial; the templating engine + does not currently provide a filter that counts the number + of lines the template generates.</para> + </listitem></itemizedlist> + <para id="x_5de">It took me no more than a minute or two of work to replace + literal text from an example of Subversion's output with some + keywords and filters to give the template above. The style + file simply refers to the template.</para> + + &interaction.template.svnstyle.style; + + <para id="x_5df">We could have included the text of the template file + directly in the style file by enclosing it in quotes and + replacing the newlines with + <quote><literal>\n</literal></quote> sequences, but it would + have made the style file too difficult to read. Readability + is a good guide when you're trying to decide whether some text + belongs in a style file, or in a template file that the style + file points to. If the style file will look too big or + cluttered if you insert a literal piece of text, drop it into + a template instead.</para> + </sect2> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch12-mq.xml --- a/fr/ch12-mq.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch12-mq.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,1309 +1,1368 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Managing change with Mercurial Queues</title> -<para>\label{chap:mq}</para> - -<sect1> -<title>The patch management problem</title> -<para>\label{sec:mq:patch-mgmt}</para> - -<para>Here is a common scenario: you need to install a software package from -source, but you find a bug that you must fix in the source before you -can start using the package. You make your changes, forget about the -package for a while, and a few months later you need to upgrade to a -newer version of the package. If the newer version of the package -still has the bug, you must extract your fix from the older source -tree and apply it against the newer version. This is a tedious task, -and it's easy to make mistakes.</para> - -<para>This is a simple case of the <quote>patch management</quote> problem. You have -an <quote>upstream</quote> source tree that you can't change; you need to make -some local changes on top of the upstream tree; and you'd like to be -able to keep those changes separate, so that you can apply them to -newer versions of the upstream source.</para> - -<para>The patch management problem arises in many situations. Probably the -most visible is that a user of an open source software project will -contribute a bug fix or new feature to the project's maintainers in the -form of a patch.</para> - -<para>Distributors of operating systems that include open source software -often need to make changes to the packages they distribute so that -they will build properly in their environments.</para> - -<para>When you have few changes to maintain, it is easy to manage a single -patch using the standard <command>diff</command> and <command>patch</command> programs -(see section <xref linkend="sec:mq:patch"/> for a discussion of these tools). -Once the number of changes grows, it starts to make sense to maintain -patches as discrete <quote>chunks of work,</quote> so that for example a single -patch will contain only one bug fix (the patch might modify several -files, but it's doing <quote>only one thing</quote>), and you may have a number -of such patches for different bugs you need fixed and local changes -you require. In this situation, if you submit a bug fix patch to the -upstream maintainers of a package and they include your fix in a -subsequent release, you can simply drop that single patch when you're -updating to the newer release.</para> - -<para>Maintaining a single patch against an upstream tree is a little -tedious and error-prone, but not difficult. However, the complexity -of the problem grows rapidly as the number of patches you have to -maintain increases. With more than a tiny number of patches in hand, -understanding which ones you have applied and maintaining them moves -from messy to overwhelming.</para> - -<para>Fortunately, Mercurial includes a powerful extension, Mercurial Queues -(or simply <quote>MQ</quote>), that massively simplifies the patch management -problem. -</para> - -</sect1> -<sect1> -<title>The prehistory of Mercurial Queues</title> -<para>\label{sec:mq:history} -</para> - -<para>During the late 1990s, several Linux kernel developers started to -maintain <quote>patch series</quote> that modified the behaviour of the Linux -kernel. Some of these series were focused on stability, some on -feature coverage, and others were more speculative. -</para> - -<para>The sizes of these patch series grew rapidly. In 2002, Andrew Morton -published some shell scripts he had been using to automate the task of -managing his patch queues. Andrew was successfully using these -scripts to manage hundreds (sometimes thousands) of patches on top of -the Linux kernel. -</para> - -<sect2> -<title>A patchwork quilt</title> -<para>\label{sec:mq:quilt} -</para> - -<para>In early 2003, Andreas Gruenbacher and Martin Quinson borrowed the -approach of Andrew's scripts and published a tool called <quote>patchwork -quilt</quote> <citation>web:quilt</citation>, or simply <quote>quilt</quote> -(see <citation>gruenbacher:2005</citation> for a paper describing it). Because -quilt substantially automated patch management, it rapidly gained a -large following among open source software developers. -</para> - -<para>Quilt manages a <emphasis>stack of patches</emphasis> on top of a directory tree. -To begin, you tell quilt to manage a directory tree, and tell it which -files you want to manage; it stores away the names and contents of -those files. To fix a bug, you create a new patch (using a single -command), edit the files you need to fix, then <quote>refresh</quote> the patch. -</para> - -<para>The refresh step causes quilt to scan the directory tree; it updates -the patch with all of the changes you have made. You can create -another patch on top of the first, which will track the changes -required to modify the tree from <quote>tree with one patch applied</quote> to -<quote>tree with two patches applied</quote>. -</para> - -<para>You can <emphasis>change</emphasis> which patches are applied to the tree. If you -<quote>pop</quote> a patch, the changes made by that patch will vanish from the -directory tree. Quilt remembers which patches you have popped, -though, so you can <quote>push</quote> a popped patch again, and the directory -tree will be restored to contain the modifications in the patch. Most -importantly, you can run the <quote>refresh</quote> command at any time, and the -topmost applied patch will be updated. This means that you can, at -any time, change both which patches are applied and what -modifications those patches make. -</para> - -<para>Quilt knows nothing about revision control tools, so it works equally -well on top of an unpacked tarball or a Subversion working copy. -</para> - -</sect2> -<sect2> -<title>From patchwork quilt to Mercurial Queues</title> -<para>\label{sec:mq:quilt-mq} -</para> - -<para>In mid-2005, Chris Mason took the features of quilt and wrote an -extension that he called Mercurial Queues, which added quilt-like -behaviour to Mercurial. -</para> - -<para>The key difference between quilt and MQ is that quilt knows nothing -about revision control systems, while MQ is <emphasis>integrated</emphasis> into -Mercurial. Each patch that you push is represented as a Mercurial -changeset. Pop a patch, and the changeset goes away. -</para> - -<para>Because quilt does not care about revision control tools, it is still -a tremendously useful piece of software to know about for situations -where you cannot use Mercurial and MQ. -</para> - -</sect2> -</sect1> -<sect1> -<title>The huge advantage of MQ</title> - -<para>I cannot overstate the value that MQ offers through the unification of -patches and revision control. -</para> - -<para>A major reason that patches have persisted in the free software and -open source world&emdash;in spite of the availability of increasingly -capable revision control tools over the years&emdash;is the <emphasis>agility</emphasis> -they offer. -</para> - -<para>Traditional revision control tools make a permanent, irreversible -record of everything that you do. While this has great value, it's -also somewhat stifling. If you want to perform a wild-eyed -experiment, you have to be careful in how you go about it, or you risk -leaving unneeded&emdash;or worse, misleading or destabilising&emdash;traces of -your missteps and errors in the permanent revision record. -</para> - -<para>By contrast, MQ's marriage of distributed revision control with -patches makes it much easier to isolate your work. Your patches live -on top of normal revision history, and you can make them disappear or -reappear at will. If you don't like a patch, you can drop it. If a -patch isn't quite as you want it to be, simply fix it&emdash;as many times -as you need to, until you have refined it into the form you desire. -</para> - -<para>As an example, the integration of patches with revision control makes -understanding patches and debugging their effects&emdash;and their -interplay with the code they're based on&emdash;<emphasis>enormously</emphasis> easier. -Since every applied patch has an associated changeset, you can use -<command role="hg-cmd">hg log <emphasis>filename</emphasis></command> to see which changesets and patches -affected a file. You can use the <literal role="hg-ext">bisect</literal> command to -binary-search through all changesets and applied patches to see where -a bug got introduced or fixed. You can use the <command role="hg-cmd">hg annotate</command> -command to see which changeset or patch modified a particular line of -a source file. And so on. -</para> - -</sect1> -<sect1> -<title>Understanding patches</title> -<para>\label{sec:mq:patch} -</para> - -<para>Because MQ doesn't hide its patch-oriented nature, it is helpful to -understand what patches are, and a little about the tools that work -with them. -</para> - -<para>The traditional Unix <command>diff</command> command compares two files, and -prints a list of differences between them. The <command>patch</command> command -understands these differences as <emphasis>modifications</emphasis> to make to a -file. Take a look at figure <xref linkend="ex:mq:diff"/> for a simple example of -these commands in action. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.dodiff.diff; --> - <caption><para>Simple uses of the <command>diff</para></caption> and \command{patch</command> commands} - \label{ex:mq:diff} -</para> -</informalfigure> - -<para>The type of file that <command>diff</command> generates (and <command>patch</command> -takes as input) is called a <quote>patch</quote> or a <quote>diff</quote>; there is no -difference between a patch and a diff. (We'll use the term <quote>patch</quote>, -since it's more commonly used.) -</para> - -<para>A patch file can start with arbitrary text; the <command>patch</command> -command ignores this text, but MQ uses it as the commit message when -creating changesets. To find the beginning of the patch content, -<command>patch</command> searches for the first line that starts with the -string <quote><literal>diff -</literal></quote>. -</para> - -<para>MQ works with <emphasis>unified</emphasis> diffs (<command>patch</command> can accept several -other diff formats, but MQ doesn't). A unified diff contains two -kinds of header. The <emphasis>file header</emphasis> describes the file being -modified; it contains the name of the file to modify. When -<command>patch</command> sees a new file header, it looks for a file with that -name to start modifying. -</para> - -<para>After the file header comes a series of <emphasis>hunks</emphasis>. Each hunk -starts with a header; this identifies the range of line numbers within -the file that the hunk should modify. Following the header, a hunk -starts and ends with a few (usually three) lines of text from the -unmodified file; these are called the <emphasis>context</emphasis> for the hunk. If -there's only a small amount of context between successive hunks, -<command>diff</command> doesn't print a new hunk header; it just runs the hunks -together, with a few lines of context between modifications. -</para> - -<para>Each line of context begins with a space character. Within the hunk, -a line that begins with <quote><literal>-</literal></quote> means <quote>remove this line,</quote> -while a line that begins with <quote><literal>+</literal></quote> means <quote>insert this -line.</quote> For example, a line that is modified is represented by one -deletion and one insertion. -</para> - -<para>We will return to some of the more subtle aspects of patches later (in -section <xref linkend="sec:mq:adv-patch"/>), but you should have enough information -now to use MQ. -</para> - -</sect1> -<sect1> -<title>Getting started with Mercurial Queues</title> -<para>\label{sec:mq:start} -</para> - -<para>Because MQ is implemented as an extension, you must explicitly enable -before you can use it. (You don't need to download anything; MQ ships -with the standard Mercurial distribution.) To enable MQ, edit your -<filename role="home">~/.hgrc</filename> file, and add the lines in figure <xref linkend="ex:mq:config"/>. -</para> - -<informalfigure> -<programlisting> -<para> [extensions] - hgext.mq = -</para> -</programlisting> -<para> \label{ex:mq:config} - <caption><para>Contents to add to <filename role="home">~/.hgrc</para></caption> to enable the MQ extension</filename> -</para> -</informalfigure> - -<para>Once the extension is enabled, it will make a number of new commands -available. To verify that the extension is working, you can use -<command role="hg-cmd">hg help</command> to see if the <command role="hg-ext-mq">qinit</command> command is now available; see -the example in figure <xref linkend="ex:mq:enabled"/>. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.qinit-help.help; --> - <caption><para>How to verify that MQ is enabled</para></caption> - \label{ex:mq:enabled} -</para> -</informalfigure> - -<para>You can use MQ with <emphasis>any</emphasis> Mercurial repository, and its commands -only operate within that repository. To get started, simply prepare -the repository using the <command role="hg-ext-mq">qinit</command> command (see -figure <xref linkend="ex:mq:qinit"/>). This command creates an empty directory -called <filename role="special" class="directory">.hg/patches</filename>, where MQ will keep its metadata. As -with many Mercurial commands, the <command role="hg-ext-mq">qinit</command> command prints nothing -if it succeeds. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qinit; --> - <caption><para>Preparing a repository for use with MQ</para></caption> - \label{ex:mq:qinit} -</para> -</informalfigure> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qnew; --> - <caption><para>Creating a new patch</para></caption> - \label{ex:mq:qnew} -</para> -</informalfigure> - -<sect2> -<title>Creating a new patch</title> - -<para>To begin work on a new patch, use the <command role="hg-ext-mq">qnew</command> command. This -command takes one argument, the name of the patch to create. MQ will -use this as the name of an actual file in the <filename role="special" class="directory">.hg/patches</filename> -directory, as you can see in figure <xref linkend="ex:mq:qnew"/>. -</para> - -<para>Also newly present in the <filename role="special" class="directory">.hg/patches</filename> directory are two -other files, <filename role="special">series</filename> and <filename role="special">status</filename>. The -<filename role="special">series</filename> file lists all of the patches that MQ knows about -for this repository, with one patch per line. Mercurial uses the -<filename role="special">status</filename> file for internal book-keeping; it tracks all of the -patches that MQ has <emphasis>applied</emphasis> in this repository. -</para> - -<note> -<para> You may sometimes want to edit the <filename role="special">series</filename> file by hand; - for example, to change the sequence in which some patches are - applied. However, manually editing the <filename role="special">status</filename> file is - almost always a bad idea, as it's easy to corrupt MQ's idea of what - is happening. -</para> -</note> - -<para>Once you have created your new patch, you can edit files in the -working directory as you usually would. All of the normal Mercurial -commands, such as <command role="hg-cmd">hg diff</command> and <command role="hg-cmd">hg annotate</command>, work exactly as -they did before. -</para> - -</sect2> -<sect2> -<title>Refreshing a patch</title> - -<para>When you reach a point where you want to save your work, use the -<command role="hg-ext-mq">qrefresh</command> command (figure <xref linkend="ex:mq:qnew"/>) to update the patch -you are working on. This command folds the changes you have made in -the working directory into your patch, and updates its corresponding -changeset to contain those changes. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qrefresh; --> - <caption><para>Refreshing a patch</para></caption> - \label{ex:mq:qrefresh} -</para> -</informalfigure> - -<para>You can run <command role="hg-ext-mq">qrefresh</command> as often as you like, so it's a good way -to <quote>checkpoint</quote> your work. Refresh your patch at an opportune -time; try an experiment; and if the experiment doesn't work out, -<command role="hg-cmd">hg revert</command> your modifications back to the last time you refreshed. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qrefresh2; --> - <caption><para>Refresh a patch many times to accumulate changes</para></caption> - \label{ex:mq:qrefresh2} -</para> -</informalfigure> - -</sect2> -<sect2> -<title>Stacking and tracking patches</title> - -<para>Once you have finished working on a patch, or need to work on another, -you can use the <command role="hg-ext-mq">qnew</command> command again to create a new patch. -Mercurial will apply this patch on top of your existing patch. See -figure <xref linkend="ex:mq:qnew2"/> for an example. Notice that the patch -contains the changes in our prior patch as part of its context (you -can see this more clearly in the output of <command role="hg-cmd">hg annotate</command>). -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qnew2; --> - <caption><para>Stacking a second patch on top of the first</para></caption> - \label{ex:mq:qnew2} -</para> -</informalfigure> - -<para>So far, with the exception of <command role="hg-ext-mq">qnew</command> and <command role="hg-ext-mq">qrefresh</command>, we've -been careful to only use regular Mercurial commands. However, MQ -provides many commands that are easier to use when you are thinking -about patches, as illustrated in figure <xref linkend="ex:mq:qseries"/>: -</para> - -<itemizedlist> -<listitem><para>The <command role="hg-ext-mq">qseries</command> command lists every patch that MQ knows - about in this repository, from oldest to newest (most recently - <emphasis>created</emphasis>). -</para> -</listitem> -<listitem><para>The <command role="hg-ext-mq">qapplied</command> command lists every patch that MQ has - <emphasis>applied</emphasis> in this repository, again from oldest to newest (most - recently applied). -</para> -</listitem></itemizedlist> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qseries; --> - \caption{Understanding the patch stack with <command role="hg-ext-mq">qseries</command> and - <command role="hg-ext-mq">qapplied</command>} - \label{ex:mq:qseries} -</para> -</informalfigure> - -</sect2> -<sect2> -<title>Manipulating the patch stack</title> - -<para>The previous discussion implied that there must be a difference -between <quote>known</quote> and <quote>applied</quote> patches, and there is. MQ can -manage a patch without it being applied in the repository. -</para> - -<para>An <emphasis>applied</emphasis> patch has a corresponding changeset in the -repository, and the effects of the patch and changeset are visible in -the working directory. You can undo the application of a patch using -the <command role="hg-ext-mq">qpop</command> command. MQ still <emphasis>knows about</emphasis>, or manages, a -popped patch, but the patch no longer has a corresponding changeset in -the repository, and the working directory does not contain the changes -made by the patch. Figure <xref linkend="fig:mq:stack"/> illustrates the -difference between applied and tracked patches. -</para> - -<informalfigure> - -<para> <mediaobject><imageobject><imagedata fileref="mq-stack"/></imageobject><textobject><phrase>XXX add text</phrase></textobject></mediaobject> - <caption><para>Applied and unapplied patches in the MQ patch stack</para></caption> - \label{fig:mq:stack} -</para> -</informalfigure> - -<para>You can reapply an unapplied, or popped, patch using the <command role="hg-ext-mq">qpush</command> -command. This creates a new changeset to correspond to the patch, and -the patch's changes once again become present in the working -directory. See figure <xref linkend="ex:mq:qpop"/> for examples of <command role="hg-ext-mq">qpop</command> -and <command role="hg-ext-mq">qpush</command> in action. Notice that once we have popped a patch -or two patches, the output of <command role="hg-ext-mq">qseries</command> remains the same, while -that of <command role="hg-ext-mq">qapplied</command> has changed. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qpop; --> - <caption><para>Modifying the stack of applied patches</para></caption> - \label{ex:mq:qpop} -</para> -</informalfigure> - -</sect2> -<sect2> -<title>Pushing and popping many patches</title> - -<para>While <command role="hg-ext-mq">qpush</command> and <command role="hg-ext-mq">qpop</command> each operate on a single patch at -a time by default, you can push and pop many patches in one go. The -<option role="hg-ext-mq-cmd-qpush-opt">-a</option> option to <command role="hg-ext-mq">qpush</command> causes it to push all -unapplied patches, while the <option role="hg-ext-mq-cmd-qpop-opt">-a</option> option to <command role="hg-ext-mq">qpop</command> -causes it to pop all applied patches. (For some more ways to push and -pop many patches, see section <xref linkend="sec:mq:perf"/> below.) -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.qpush-a; --> - <caption><para>Pushing all unapplied patches</para></caption> - \label{ex:mq:qpush-a} -</para> -</informalfigure> - -</sect2> -<sect2> -<title>Safety checks, and overriding them</title> - -<para>Several MQ commands check the working directory before they do -anything, and fail if they find any modifications. They do this to -ensure that you won't lose any changes that you have made, but not yet -incorporated into a patch. Figure <xref linkend="ex:mq:add"/> illustrates this; -the <command role="hg-ext-mq">qnew</command> command will not create a new patch if there are -outstanding changes, caused in this case by the <command role="hg-cmd">hg add</command> of -<filename>file3</filename>. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tutorial.add; --> - <caption><para>Forcibly creating a patch</para></caption> - \label{ex:mq:add} -</para> -</informalfigure> - -<para>Commands that check the working directory all take an <quote>I know what -I'm doing</quote> option, which is always named <option>-f</option>. The exact -meaning of <option>-f</option> depends on the command. For example, -<command role="hg-cmd">hg qnew <option role="hg-ext-mq-cmd-qnew-opt">-f</option></command> will incorporate any outstanding -changes into the new patch it creates, but -<command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-f</option></command> will revert modifications to any -files affected by the patch that it is popping. Be sure to read the -documentation for a command's <option>-f</option> option before you use it! -</para> - -</sect2> -<sect2> -<title>Working on several patches at once</title> - -<para>The <command role="hg-ext-mq">qrefresh</command> command always refreshes the <emphasis>topmost</emphasis> -applied patch. This means that you can suspend work on one patch (by -refreshing it), pop or push to make a different patch the top, and -work on <emphasis>that</emphasis> patch for a while. -</para> - -<para>Here's an example that illustrates how you can use this ability. -Let's say you're developing a new feature as two patches. The first -is a change to the core of your software, and the second&emdash;layered on -top of the first&emdash;changes the user interface to use the code you just -added to the core. If you notice a bug in the core while you're -working on the UI patch, it's easy to fix the core. Simply -<command role="hg-ext-mq">qrefresh</command> the UI patch to save your in-progress changes, and -<command role="hg-ext-mq">qpop</command> down to the core patch. Fix the core bug, -<command role="hg-ext-mq">qrefresh</command> the core patch, and <command role="hg-ext-mq">qpush</command> back to the UI -patch to continue where you left off. -</para> - -</sect2> -</sect1> -<sect1> -<title>More about patches</title> -<para>\label{sec:mq:adv-patch} -</para> - -<para>MQ uses the GNU <command>patch</command> command to apply patches, so it's -helpful to know a few more detailed aspects of how <command>patch</command> -works, and about patches themselves. -</para> - -<sect2> -<title>The strip count</title> - -<para>If you look at the file headers in a patch, you will notice that the -pathnames usually have an extra component on the front that isn't -present in the actual path name. This is a holdover from the way that -people used to generate patches (people still do this, but it's -somewhat rare with modern revision control tools). -</para> - -<para>Alice would unpack a tarball, edit her files, then decide that she -wanted to create a patch. So she'd rename her working directory, -unpack the tarball again (hence the need for the rename), and use the -<option role="cmd-opt-diff">-r</option> and <option role="cmd-opt-diff">-N</option> options to <command>diff</command> to -recursively generate a patch between the unmodified directory and the -modified one. The result would be that the name of the unmodified -directory would be at the front of the left-hand path in every file -header, and the name of the modified directory would be at the front -of the right-hand path. -</para> - -<para>Since someone receiving a patch from the Alices of the net would be -unlikely to have unmodified and modified directories with exactly the -same names, the <command>patch</command> command has a <option role="cmd-opt-patch">-p</option> -option that indicates the number of leading path name components to -strip when trying to apply a patch. This number is called the -<emphasis>strip count</emphasis>. -</para> - -<para>An option of <quote><literal>-p1</literal></quote> means <quote>use a strip count of one</quote>. If -<command>patch</command> sees a file name <filename>foo/bar/baz</filename> in a file -header, it will strip <filename>foo</filename> and try to patch a file named -<filename>bar/baz</filename>. (Strictly speaking, the strip count refers to the -number of <emphasis>path separators</emphasis> (and the components that go with them -) to strip. A strip count of one will turn <filename>foo/bar</filename> into -<filename>bar</filename>, but <filename>/foo/bar</filename> (notice the extra leading -slash) into <filename>foo/bar</filename>.) -</para> - -<para>The <quote>standard</quote> strip count for patches is one; almost all patches -contain one leading path name component that needs to be stripped. -Mercurial's <command role="hg-cmd">hg diff</command> command generates path names in this form, -and the <command role="hg-cmd">hg import</command> command and MQ expect patches to have a strip -count of one. -</para> - -<para>If you receive a patch from someone that you want to add to your patch -queue, and the patch needs a strip count other than one, you cannot -just <command role="hg-ext-mq">qimport</command> the patch, because <command role="hg-ext-mq">qimport</command> does not yet -have a <literal>-p</literal> option (see <ulink role="hg-bug" url="http://www.selenic.com/mercurial/bts/issue311">issue 311</ulink>). Your best bet is to -<command role="hg-ext-mq">qnew</command> a patch of your own, then use <command>patch -p<emphasis>N</emphasis></command> -to apply their patch, followed by <command role="hg-cmd">hg addremove</command> to pick up any -files added or removed by the patch, followed by <command role="hg-ext-mq">qrefresh</command>. -This complexity may become unnecessary; see <ulink role="hg-bug" url="http://www.selenic.com/mercurial/bts/issue311">issue 311</ulink> for details. -</sect2> -<sect2> -<title>Strategies for applying a patch</title> -</para> - -<para>When <command>patch</command> applies a hunk, it tries a handful of -successively less accurate strategies to try to make the hunk apply. -This falling-back technique often makes it possible to take a patch -that was generated against an old version of a file, and apply it -against a newer version of that file. -</para> - -<para>First, <command>patch</command> tries an exact match, where the line numbers, -the context, and the text to be modified must apply exactly. If it -cannot make an exact match, it tries to find an exact match for the -context, without honouring the line numbering information. If this -succeeds, it prints a line of output saying that the hunk was applied, -but at some <emphasis>offset</emphasis> from the original line number. -</para> - -<para>If a context-only match fails, <command>patch</command> removes the first and -last lines of the context, and tries a <emphasis>reduced</emphasis> context-only -match. If the hunk with reduced context succeeds, it prints a message -saying that it applied the hunk with a <emphasis>fuzz factor</emphasis> (the number -after the fuzz factor indicates how many lines of context -<command>patch</command> had to trim before the patch applied). -</para> - -<para>When neither of these techniques works, <command>patch</command> prints a -message saying that the hunk in question was rejected. It saves -rejected hunks (also simply called <quote>rejects</quote>) to a file with the -same name, and an added <filename role="special">.rej</filename> extension. It also saves an -unmodified copy of the file with a <filename role="special">.orig</filename> extension; the -copy of the file without any extensions will contain any changes made -by hunks that <emphasis>did</emphasis> apply cleanly. If you have a patch that -modifies <filename>foo</filename> with six hunks, and one of them fails to -apply, you will have: an unmodified <filename>foo.orig</filename>, a -<filename>foo.rej</filename> containing one hunk, and <filename>foo</filename>, containing -the changes made by the five successful hunks. -</para> - -</sect2> -<sect2> -<title>Some quirks of patch representation</title> - -<para>There are a few useful things to know about how <command>patch</command> works -with files. -</para> -<itemizedlist> -<listitem><para>This should already be obvious, but <command>patch</command> cannot - handle binary files. -</para> -</listitem> -<listitem><para>Neither does it care about the executable bit; it creates new - files as readable, but not executable. -</para> -</listitem> -<listitem><para><command>patch</command> treats the removal of a file as a diff between - the file to be removed and the empty file. So your idea of <quote>I - deleted this file</quote> looks like <quote>every line of this file was - deleted</quote> in a patch. -</para> -</listitem> -<listitem><para>It treats the addition of a file as a diff between the empty - file and the file to be added. So in a patch, your idea of <quote>I - added this file</quote> looks like <quote>every line of this file was added</quote>. -</para> -</listitem> -<listitem><para>It treats a renamed file as the removal of the old name, and the - addition of the new name. This means that renamed files have a big - footprint in patches. (Note also that Mercurial does not currently - try to infer when files have been renamed or copied in a patch.) -</para> -</listitem> -<listitem><para><command>patch</command> cannot represent empty files, so you cannot use - a patch to represent the notion <quote>I added this empty file to the - tree</quote>. -</para> -</listitem></itemizedlist> -</sect2> -<sect2> -<title>Beware the fuzz</title> - -<para>While applying a hunk at an offset, or with a fuzz factor, will often -be completely successful, these inexact techniques naturally leave -open the possibility of corrupting the patched file. The most common -cases typically involve applying a patch twice, or at an incorrect -location in the file. If <command>patch</command> or <command role="hg-ext-mq">qpush</command> ever -mentions an offset or fuzz factor, you should make sure that the -modified files are correct afterwards. -</para> - -<para>It's often a good idea to refresh a patch that has applied with an -offset or fuzz factor; refreshing the patch generates new context -information that will make it apply cleanly. I say <quote>often,</quote> not -<quote>always,</quote> because sometimes refreshing a patch will make it fail to -apply against a different revision of the underlying files. In some -cases, such as when you're maintaining a patch that must sit on top of -multiple versions of a source tree, it's acceptable to have a patch -apply with some fuzz, provided you've verified the results of the -patching process in such cases. -</para> - -</sect2> -<sect2> -<title>Handling rejection</title> - -<para>If <command role="hg-ext-mq">qpush</command> fails to apply a patch, it will print an error -message and exit. If it has left <filename role="special">.rej</filename> files behind, it is -usually best to fix up the rejected hunks before you push more patches -or do any further work. -</para> - -<para>If your patch <emphasis>used to</emphasis> apply cleanly, and no longer does because -you've changed the underlying code that your patches are based on, -Mercurial Queues can help; see section <xref linkend="sec:mq:merge"/> for details. -</para> - -<para>Unfortunately, there aren't any great techniques for dealing with -rejected hunks. Most often, you'll need to view the <filename role="special">.rej</filename> -file and edit the target file, applying the rejected hunks by hand. -</para> - -<para>If you're feeling adventurous, Neil Brown, a Linux kernel hacker, -wrote a tool called <command>wiggle</command> <citation>web:wiggle</citation>, which is more -vigorous than <command>patch</command> in its attempts to make a patch apply. -</para> - -<para>Another Linux kernel hacker, Chris Mason (the author of Mercurial -Queues), wrote a similar tool called -<command>mpatch</command> <citation>web:mpatch</citation>, which takes a simple approach to -automating the application of hunks rejected by <command>patch</command>. The -<command>mpatch</command> command can help with four common reasons that a hunk -may be rejected: -</para> - -<itemizedlist> -<listitem><para>The context in the middle of a hunk has changed. -</para> -</listitem> -<listitem><para>A hunk is missing some context at the beginning or end. -</para> -</listitem> -<listitem><para>A large hunk might apply better&emdash;either entirely or in - part&emdash;if it was broken up into smaller hunks. -</para> -</listitem> -<listitem><para>A hunk removes lines with slightly different content than those - currently present in the file. -</para> -</listitem></itemizedlist> - -<para>If you use <command>wiggle</command> or <command>mpatch</command>, you should be doubly -careful to check your results when you're done. In fact, -<command>mpatch</command> enforces this method of double-checking the tool's -output, by automatically dropping you into a merge program when it has -done its job, so that you can verify its work and finish off any -remaining merges. -</para> - -</sect2> -</sect1> -<sect1> -<title>Getting the best performance out of MQ</title> -<para>\label{sec:mq:perf} -</para> - -<para>MQ is very efficient at handling a large number of patches. I ran -some performance experiments in mid-2006 for a talk that I gave at the -2006 EuroPython conference <citation>web:europython</citation>. I used as my data -set the Linux 2.6.17-mm1 patch series, which consists of 1,738 -patches. I applied these on top of a Linux kernel repository -containing all 27,472 revisions between Linux 2.6.12-rc2 and Linux -2.6.17. -</para> - -<para>On my old, slow laptop, I was able to -<command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpush-opt">-a</option></command> all 1,738 patches in 3.5 minutes, -and <command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-a</option></command> them all in 30 seconds. (On a -newer laptop, the time to push all patches dropped to two minutes.) I -could <command role="hg-ext-mq">qrefresh</command> one of the biggest patches (which made 22,779 -lines of changes to 287 files) in 6.6 seconds. -</para> - -<para>Clearly, MQ is well suited to working in large trees, but there are a -few tricks you can use to get the best performance of it. -</para> - -<para>First of all, try to <quote>batch</quote> operations together. Every time you -run <command role="hg-ext-mq">qpush</command> or <command role="hg-ext-mq">qpop</command>, these commands scan the working -directory once to make sure you haven't made some changes and then -forgotten to run <command role="hg-ext-mq">qrefresh</command>. On a small tree, the time that -this scan takes is unnoticeable. However, on a medium-sized tree -(containing tens of thousands of files), it can take a second or more. -</para> - -<para>The <command role="hg-ext-mq">qpush</command> and <command role="hg-ext-mq">qpop</command> commands allow you to push and pop -multiple patches at a time. You can identify the <quote>destination -patch</quote> that you want to end up at. When you <command role="hg-ext-mq">qpush</command> with a -destination specified, it will push patches until that patch is at the -top of the applied stack. When you <command role="hg-ext-mq">qpop</command> to a destination, MQ -will pop patches until the destination patch is at the top. -</para> - -<para>You can identify a destination patch using either the name of the -patch, or by number. If you use numeric addressing, patches are -counted from zero; this means that the first patch is zero, the second -is one, and so on. -</para> - -</sect1> -<sect1> -<title>Updating your patches when the underlying code changes</title> -<para>\label{sec:mq:merge} -</para> - -<para>It's common to have a stack of patches on top of an underlying -repository that you don't modify directly. If you're working on -changes to third-party code, or on a feature that is taking longer to -develop than the rate of change of the code beneath, you will often -need to sync up with the underlying code, and fix up any hunks in your -patches that no longer apply. This is called <emphasis>rebasing</emphasis> your -patch series. -</para> - -<para>The simplest way to do this is to <command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-a</option></command> -your patches, then <command role="hg-cmd">hg pull</command> changes into the underlying -repository, and finally <command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpop-opt">-a</option></command> your -patches again. MQ will stop pushing any time it runs across a patch -that fails to apply during conflicts, allowing you to fix your -conflicts, <command role="hg-ext-mq">qrefresh</command> the affected patch, and continue pushing -until you have fixed your entire stack. -</para> - -<para>This approach is easy to use and works well if you don't expect -changes to the underlying code to affect how well your patches apply. -If your patch stack touches code that is modified frequently or -invasively in the underlying repository, however, fixing up rejected -hunks by hand quickly becomes tiresome. -</para> - -<para>It's possible to partially automate the rebasing process. If your -patches apply cleanly against some revision of the underlying repo, MQ -can use this information to help you to resolve conflicts between your -patches and a different revision. -</para> - -<para>The process is a little involved. -</para> -<orderedlist> -<listitem><para>To begin, <command role="hg-cmd">hg qpush -a</command> all of your patches on top of - the revision where you know that they apply cleanly. -</para> -</listitem> -<listitem><para>Save a backup copy of your patch directory using - <command role="hg-cmd">hg qsave <option role="hg-ext-mq-cmd-qsave-opt">-e</option> <option role="hg-ext-mq-cmd-qsave-opt">-c</option></command>. This prints - the name of the directory that it has saved the patches in. It will - save the patches to a directory called - <filename role="special" class="directory">.hg/patches.<emphasis>N</filename></emphasis>, where <literal><emphasis>N</emphasis></literal> is a small - integer. It also commits a <quote>save changeset</quote> on top of your - applied patches; this is for internal book-keeping, and records the - states of the <filename role="special">series</filename> and <filename role="special">status</filename> files. -</para> -</listitem> -<listitem><para>Use <command role="hg-cmd">hg pull</command> to bring new changes into the underlying - repository. (Don't run <command role="hg-cmd">hg pull -u</command>; see below for why.) -</para> -</listitem> -<listitem><para>Update to the new tip revision, using - <command role="hg-cmd">hg update <option role="hg-opt-update">-C</option></command> to override the patches you - have pushed. -</para> -</listitem> -<listitem><para>Merge all patches using \hgcmdargs{qpush}{<option role="hg-ext-mq-cmd-qpush-opt">-m</option> - <option role="hg-ext-mq-cmd-qpush-opt">-a</option>}. The <option role="hg-ext-mq-cmd-qpush-opt">-m</option> option to <command role="hg-ext-mq">qpush</command> - tells MQ to perform a three-way merge if the patch fails to apply. -</para> -</listitem></orderedlist> - -<para>During the <command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpush-opt">-m</option></command>, each patch in the -<filename role="special">series</filename> file is applied normally. If a patch applies with -fuzz or rejects, MQ looks at the queue you <command role="hg-ext-mq">qsave</command>d, and -performs a three-way merge with the corresponding changeset. This -merge uses Mercurial's normal merge machinery, so it may pop up a GUI -merge tool to help you to resolve problems. -</para> - -<para>When you finish resolving the effects of a patch, MQ refreshes your -patch based on the result of the merge. -</para> - -<para>At the end of this process, your repository will have one extra head -from the old patch queue, and a copy of the old patch queue will be in -<filename role="special" class="directory">.hg/patches.<emphasis>N</filename></emphasis>. You can remove the extra head using -<command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-a</option> <option role="hg-ext-mq-cmd-qpop-opt">-n</option> patches.<emphasis>N</emphasis></command> -or <command role="hg-cmd">hg strip</command>. You can delete <filename role="special" class="directory">.hg/patches.<emphasis>N</filename></emphasis> once -you are sure that you no longer need it as a backup. -</para> - -</sect1> -<sect1> -<title>Identifying patches</title> - -<para>MQ commands that work with patches let you refer to a patch either by -using its name or by a number. By name is obvious enough; pass the -name <filename>foo.patch</filename> to <command role="hg-ext-mq">qpush</command>, for example, and it will -push patches until <filename>foo.patch</filename> is applied. -</para> - -<para>As a shortcut, you can refer to a patch using both a name and a -numeric offset; <literal>foo.patch-2</literal> means <quote>two patches before -<literal>foo.patch</literal></quote>, while <literal>bar.patch+4</literal> means <quote>four patches -after <literal>bar.patch</literal></quote>. -</para> - -<para>Referring to a patch by index isn't much different. The first patch -printed in the output of <command role="hg-ext-mq">qseries</command> is patch zero (yes, it's one -of those start-at-zero counting systems); the second is patch one; and -so on. -</para> - -<para>MQ also makes it easy to work with patches when you are using normal -Mercurial commands. Every command that accepts a changeset ID will -also accept the name of an applied patch. MQ augments the tags -normally in the repository with an eponymous one for each applied -patch. In addition, the special tags \index{tags!special tag - names!<literal>qbase</literal>}<literal>qbase</literal> and \index{tags!special tag - names!<literal>qtip</literal>}<literal>qtip</literal> identify the <quote>bottom-most</quote> and -topmost applied patches, respectively. -</para> - -<para>These additions to Mercurial's normal tagging capabilities make -dealing with patches even more of a breeze. -</para> -<itemizedlist> -<listitem><para>Want to patchbomb a mailing list with your latest series of - changes? -</para> -</listitem><programlisting> -<listitem><para> hg email qbase:qtip -</para> -</listitem></programlisting> -<listitem><para> (Don't know what <quote>patchbombing</quote> is? See - section <xref linkend="sec:hgext:patchbomb"/>.) -</para> -</listitem> -<listitem><para>Need to see all of the patches since <literal>foo.patch</literal> that - have touched files in a subdirectory of your tree? -</para> -</listitem><programlisting> -<listitem><para> hg log -r foo.patch:qtip <emphasis>subdir</emphasis> -</para> -</listitem></programlisting> -</itemizedlist> - -<para>Because MQ makes the names of patches available to the rest of -Mercurial through its normal internal tag machinery, you don't need to -type in the entire name of a patch when you want to identify it by -name. -</para> - -<informalfigure> -<para> <!-- &interaction.mq.id.output; --> - <caption><para>Using MQ's tag features to work with patches</para></caption> - \label{ex:mq:id} -</para> -</informalfigure> - -<para>Another nice consequence of representing patch names as tags is that -when you run the <command role="hg-cmd">hg log</command> command, it will display a patch's name -as a tag, simply as part of its normal output. This makes it easy to -visually distinguish applied patches from underlying <quote>normal</quote> -revisions. Figure <xref linkend="ex:mq:id"/> shows a few normal Mercurial -commands in use with applied patches. -</para> - -</sect1> -<sect1> -<title>Useful things to know about</title> - -<para>There are a number of aspects of MQ usage that don't fit tidily into -sections of their own, but that are good to know. Here they are, in -one place. -</para> - -<itemizedlist> -<listitem><para>Normally, when you <command role="hg-ext-mq">qpop</command> a patch and <command role="hg-ext-mq">qpush</command> it - again, the changeset that represents the patch after the pop/push - will have a <emphasis>different identity</emphasis> than the changeset that - represented the hash beforehand. See - section <xref linkend="sec:mqref:cmd:qpush"/> for information as to why this is. -</para> -</listitem> -<listitem><para>It's not a good idea to <command role="hg-cmd">hg merge</command> changes from another - branch with a patch changeset, at least if you want to maintain the - <quote>patchiness</quote> of that changeset and changesets below it on the - patch stack. If you try to do this, it will appear to succeed, but - MQ will become confused. -</para> -</listitem></itemizedlist> - -</sect1> -<sect1> -<title>Managing patches in a repository</title> -<para>\label{sec:mq:repo} -</para> - -<para>Because MQ's <filename role="special" class="directory">.hg/patches</filename> directory resides outside a -Mercurial repository's working directory, the <quote>underlying</quote> Mercurial -repository knows nothing about the management or presence of patches. -</para> - -<para>This presents the interesting possibility of managing the contents of -the patch directory as a Mercurial repository in its own right. This -can be a useful way to work. For example, you can work on a patch for -a while, <command role="hg-ext-mq">qrefresh</command> it, then <command role="hg-cmd">hg commit</command> the current state of -the patch. This lets you <quote>roll back</quote> to that version of the patch -later on. -</para> - -<para>You can then share different versions of the same patch stack among -multiple underlying repositories. I use this when I am developing a -Linux kernel feature. I have a pristine copy of my kernel sources for -each of several CPU architectures, and a cloned repository under each -that contains the patches I am working on. When I want to test a -change on a different architecture, I push my current patches to the -patch repository associated with that kernel tree, pop and push all of -my patches, and build and test that kernel. -</para> - -<para>Managing patches in a repository makes it possible for multiple -developers to work on the same patch series without colliding with -each other, all on top of an underlying source base that they may or -may not control. -</para> - -<sect2> -<title>MQ support for patch repositories</title> - -<para>MQ helps you to work with the <filename role="special" class="directory">.hg/patches</filename> directory as a -repository; when you prepare a repository for working with patches -using <command role="hg-ext-mq">qinit</command>, you can pass the <option role="hg-ext-mq-cmd-qinit-opt">-c</option> option to -create the <filename role="special" class="directory">.hg/patches</filename> directory as a Mercurial repository. -</para> - -<note> -<para> If you forget to use the <option role="hg-ext-mq-cmd-qinit-opt">-c</option> option, you can simply go - into the <filename role="special" class="directory">.hg/patches</filename> directory at any time and run - <command role="hg-cmd">hg init</command>. Don't forget to add an entry for the - <filename role="special">status</filename> file to the <filename role="special">.hgignore</filename> file, though -</para> - -<para> (<command role="hg-cmd">hg qinit <option role="hg-ext-mq-cmd-qinit-opt">-c</option></command> does this for you - automatically); you <emphasis>really</emphasis> don't want to manage the - <filename role="special">status</filename> file. -</para> -</note> - -<para>As a convenience, if MQ notices that the <filename class="directory">.hg/patches</filename> -directory is a repository, it will automatically <command role="hg-cmd">hg add</command> every -patch that you create and import. -</para> - -<para>MQ provides a shortcut command, <command role="hg-ext-mq">qcommit</command>, that runs -<command role="hg-cmd">hg commit</command> in the <filename role="special" class="directory">.hg/patches</filename> directory. This saves -some bothersome typing. -</para> - -<para>Finally, as a convenience to manage the patch directory, you can -define the alias <command>mq</command> on Unix systems. For example, on Linux -systems using the <command>bash</command> shell, you can include the following -snippet in your <filename role="home">~/.bashrc</filename>. -</para> - -<programlisting> -<para> alias mq=`hg -R $(hg root)/.hg/patches' -</para> -</programlisting> - -<para>You can then issue commands of the form <command>mq pull</command> from -the main repository. -</para> - -</sect2> -<sect2> -<title>A few things to watch out for</title> - -<para>MQ's support for working with a repository full of patches is limited -in a few small respects. -</para> - -<para>MQ cannot automatically detect changes that you make to the patch -directory. If you <command role="hg-cmd">hg pull</command>, manually edit, or <command role="hg-cmd">hg update</command> -changes to patches or the <filename role="special">series</filename> file, you will have to -<command role="hg-cmd">hg qpop <option role="hg-ext-mq-cmd-qpop-opt">-a</option></command> and then -<command role="hg-cmd">hg qpush <option role="hg-ext-mq-cmd-qpush-opt">-a</option></command> in the underlying repository to -see those changes show up there. If you forget to do this, you can -confuse MQ's idea of which patches are applied. -</para> - -</sect2> -</sect1> -<sect1> -<title>Third party tools for working with patches</title> -<para>\label{sec:mq:tools} -</para> - -<para>Once you've been working with patches for a while, you'll find -yourself hungry for tools that will help you to understand and -manipulate the patches you're dealing with. -</para> - -<para>The <command>diffstat</command> command <citation>web:diffstat</citation> generates a -histogram of the modifications made to each file in a patch. It -provides a good way to <quote>get a sense of</quote> a patch&emdash;which files it -affects, and how much change it introduces to each file and as a -whole. (I find that it's a good idea to use <command>diffstat</command>'s -<option role="cmd-opt-diffstat">-p</option> option as a matter of course, as otherwise it -will try to do clever things with prefixes of file names that -inevitably confuse at least me.) -</para> - -<informalfigure> -<para> <!-- &interaction.mq.tools.tools; --> - <caption><para>The <command>diffstat</para></caption>, \command{filterdiff</command>, and <command>lsdiff</command> commands} - \label{ex:mq:tools} -</para> -</informalfigure> - -<para>The <literal role="package">patchutils</literal> package <citation>web:patchutils</citation> is invaluable. -It provides a set of small utilities that follow the <quote>Unix -philosophy;</quote> each does one useful thing with a patch. The -<literal role="package">patchutils</literal> command I use most is <command>filterdiff</command>, which -extracts subsets from a patch file. For example, given a patch that -modifies hundreds of files across dozens of directories, a single -invocation of <command>filterdiff</command> can generate a smaller patch that -only touches files whose names match a particular glob pattern. See -section <xref linkend="mq-collab:tips:interdiff"/> for another example. -</para> - -</sect1> -<sect1> -<title>Good ways to work with patches</title> - -<para>Whether you are working on a patch series to submit to a free software -or open source project, or a series that you intend to treat as a -sequence of regular changesets when you're done, you can use some -simple techniques to keep your work well organised. -</para> - -<para>Give your patches descriptive names. A good name for a patch might be -<filename>rework-device-alloc.patch</filename>, because it will immediately give -you a hint what the purpose of the patch is. Long names shouldn't be -a problem; you won't be typing the names often, but you <emphasis>will</emphasis> be -running commands like <command role="hg-ext-mq">qapplied</command> and <command role="hg-ext-mq">qtop</command> over and over. -Good naming becomes especially important when you have a number of -patches to work with, or if you are juggling a number of different -tasks and your patches only get a fraction of your attention. -</para> - -<para>Be aware of what patch you're working on. Use the <command role="hg-ext-mq">qtop</command> -command and skim over the text of your patches frequently&emdash;for -example, using <command role="hg-cmd">hg tip <option role="hg-opt-tip">-p</option></command>)&emdash;to be sure of where -you stand. I have several times worked on and <command role="hg-ext-mq">qrefresh</command>ed a -patch other than the one I intended, and it's often tricky to migrate -changes into the right patch after making them in the wrong one. -</para> - -<para>For this reason, it is very much worth investing a little time to -learn how to use some of the third-party tools I described in -section <xref linkend="sec:mq:tools"/>, particularly <command>diffstat</command> and -<command>filterdiff</command>. The former will give you a quick idea of what -changes your patch is making, while the latter makes it easy to splice -hunks selectively out of one patch and into another. -</para> - -</sect1> -<sect1> -<title>MQ cookbook</title> - -<sect2> -<title>Manage <quote>trivial</quote> patches</title> - -<para>Because the overhead of dropping files into a new Mercurial repository -is so low, it makes a lot of sense to manage patches this way even if -you simply want to make a few changes to a source tarball that you -downloaded. -</para> - -<para>Begin by downloading and unpacking the source tarball, -and turning it into a Mercurial repository. -<!-- &interaction.mq.tarball.download; --> -</para> - -<para>Continue by creating a patch stack and making your changes. -<!-- &interaction.mq.tarball.qinit; --> -</para> - -<para>Let's say a few weeks or months pass, and your package author releases -a new version. First, bring their changes into the repository. -<!-- &interaction.mq.tarball.newsource; --> -The pipeline starting with <command role="hg-cmd">hg locate</command> above deletes all files in -the working directory, so that <command role="hg-cmd">hg commit</command>'s -<option role="hg-opt-commit">--addremove</option> option can actually tell which files have -really been removed in the newer version of the source. -</para> - -<para>Finally, you can apply your patches on top of the new tree. -<!-- &interaction.mq.tarball.repush; --> -</para> - -</sect2> -<sect2> -<title>Combining entire patches</title> -<para>\label{sec:mq:combine} -</para> - -<para>MQ provides a command, <command role="hg-ext-mq">qfold</command> that lets you combine entire -patches. This <quote>folds</quote> the patches you name, in the order you name -them, into the topmost applied patch, and concatenates their -descriptions onto the end of its description. The patches that you -fold must be unapplied before you fold them. -</para> - -<para>The order in which you fold patches matters. If your topmost applied -patch is <literal>foo</literal>, and you <command role="hg-ext-mq">qfold</command> <literal>bar</literal> and -<literal>quux</literal> into it, you will end up with a patch that has the same -effect as if you applied first <literal>foo</literal>, then <literal>bar</literal>, -followed by <literal>quux</literal>. -</para> - -</sect2> -<sect2> -<title>Merging part of one patch into another</title> - -<para>Merging <emphasis>part</emphasis> of one patch into another is more difficult than -combining entire patches. -</para> - -<para>If you want to move changes to entire files, you can use -<command>filterdiff</command>'s <option role="cmd-opt-filterdiff">-i</option> and -<option role="cmd-opt-filterdiff">-x</option> options to choose the modifications to snip -out of one patch, concatenating its output onto the end of the patch -you want to merge into. You usually won't need to modify the patch -you've merged the changes from. Instead, MQ will report some rejected -hunks when you <command role="hg-ext-mq">qpush</command> it (from the hunks you moved into the -other patch), and you can simply <command role="hg-ext-mq">qrefresh</command> the patch to drop -the duplicate hunks. -</para> - -<para>If you have a patch that has multiple hunks modifying a file, and you -only want to move a few of those hunks, the job becomes more messy, -but you can still partly automate it. Use <command>lsdiff -nvv</command> to -print some metadata about the patch. -<!-- &interaction.mq.tools.lsdiff; --> -</para> - -<para>This command prints three different kinds of number: -</para> -<itemizedlist> -<listitem><para>(in the first column) a <emphasis>file number</emphasis> to identify each file - modified in the patch; -</para> -</listitem> -<listitem><para>(on the next line, indented) the line number within a modified - file where a hunk starts; and -</para> -</listitem> -<listitem><para>(on the same line) a <emphasis>hunk number</emphasis> to identify that hunk. -</para> -</listitem></itemizedlist> - -<para>You'll have to use some visual inspection, and reading of the patch, -to identify the file and hunk numbers you'll want, but you can then -pass them to to <command>filterdiff</command>'s <option role="cmd-opt-filterdiff">--files</option> -and <option role="cmd-opt-filterdiff">--hunks</option> options, to select exactly the file -and hunk you want to extract. -</para> - -<para>Once you have this hunk, you can concatenate it onto the end of your -destination patch and continue with the remainder of -section <xref linkend="sec:mq:combine"/>. -</para> - -</sect2> -</sect1> -<sect1> -<title>Differences between quilt and MQ</title> - -<para>If you are already familiar with quilt, MQ provides a similar command -set. There are a few differences in the way that it works. -</para> - -<para>You will already have noticed that most quilt commands have MQ -counterparts that simply begin with a <quote><literal>q</literal></quote>. The exceptions -are quilt's <literal>add</literal> and <literal>remove</literal> commands, the -counterparts for which are the normal Mercurial <command role="hg-cmd">hg add</command> and -<command role="hg-cmd">hg remove</command> commands. There is no MQ equivalent of the quilt -<literal>edit</literal> command. -</para> - -</sect1> +<chapter id="chap:mq"> + <?dbhtml filename="managing-change-with-mercurial-queues.html"?> + <title>Managing change with Mercurial Queues</title> + + <sect1 id="sec:mq:patch-mgmt"> + <title>The patch management problem</title> + + <para id="x_3ac">Here is a common scenario: you need to install a software + package from source, but you find a bug that you must fix in the + source before you can start using the package. You make your + changes, forget about the package for a while, and a few months + later you need to upgrade to a newer version of the package. If + the newer version of the package still has the bug, you must + extract your fix from the older source tree and apply it against + the newer version. This is a tedious task, and it's easy to + make mistakes.</para> + + <para id="x_3ad">This is a simple case of the <quote>patch management</quote> + problem. You have an <quote>upstream</quote> source tree that + you can't change; you need to make some local changes on top of + the upstream tree; and you'd like to be able to keep those + changes separate, so that you can apply them to newer versions + of the upstream source.</para> + + <para id="x_3ae">The patch management problem arises in many situations. + Probably the most visible is that a user of an open source + software project will contribute a bug fix or new feature to the + project's maintainers in the form of a patch.</para> + + <para id="x_3af">Distributors of operating systems that include open source + software often need to make changes to the packages they + distribute so that they will build properly in their + environments.</para> + + <para id="x_3b0">When you have few changes to maintain, it is easy to manage + a single patch using the standard <command>diff</command> and + <command>patch</command> programs (see <xref + linkend="sec:mq:patch"/> for a discussion of these + tools). Once the number of changes grows, it starts to make + sense to maintain patches as discrete <quote>chunks of + work,</quote> so that for example a single patch will contain + only one bug fix (the patch might modify several files, but it's + doing <quote>only one thing</quote>), and you may have a number + of such patches for different bugs you need fixed and local + changes you require. In this situation, if you submit a bug fix + patch to the upstream maintainers of a package and they include + your fix in a subsequent release, you can simply drop that + single patch when you're updating to the newer release.</para> + + <para id="x_3b1">Maintaining a single patch against an upstream tree is a + little tedious and error-prone, but not difficult. However, the + complexity of the problem grows rapidly as the number of patches + you have to maintain increases. With more than a tiny number of + patches in hand, understanding which ones you have applied and + maintaining them moves from messy to overwhelming.</para> + + <para id="x_3b2">Fortunately, Mercurial includes a powerful extension, + Mercurial Queues (or simply <quote>MQ</quote>), that massively + simplifies the patch management problem.</para> + + </sect1> + <sect1 id="sec:mq:history"> + <title>The prehistory of Mercurial Queues</title> + + <para id="x_3b3">During the late 1990s, several Linux kernel developers + started to maintain <quote>patch series</quote> that modified + the behavior of the Linux kernel. Some of these series were + focused on stability, some on feature coverage, and others were + more speculative.</para> + + <para id="x_3b4">The sizes of these patch series grew rapidly. In 2002, + Andrew Morton published some shell scripts he had been using to + automate the task of managing his patch queues. Andrew was + successfully using these scripts to manage hundreds (sometimes + thousands) of patches on top of the Linux kernel.</para> + + <sect2 id="sec:mq:quilt"> + <title>A patchwork quilt</title> + + <para id="x_3b5">In early 2003, Andreas Gruenbacher and Martin Quinson + borrowed the approach of Andrew's scripts and published a tool + called <quote>patchwork quilt</quote> + <citation>web:quilt</citation>, or simply <quote>quilt</quote> + (see <citation>gruenbacher:2005</citation> for a paper + describing it). Because quilt substantially automated patch + management, it rapidly gained a large following among open + source software developers.</para> + + <para id="x_3b6">Quilt manages a <emphasis>stack of patches</emphasis> on + top of a directory tree. To begin, you tell quilt to manage a + directory tree, and tell it which files you want to manage; it + stores away the names and contents of those files. To fix a + bug, you create a new patch (using a single command), edit the + files you need to fix, then <quote>refresh</quote> the + patch.</para> + + <para id="x_3b7">The refresh step causes quilt to scan the directory tree; + it updates the patch with all of the changes you have made. + You can create another patch on top of the first, which will + track the changes required to modify the tree from <quote>tree + with one patch applied</quote> to <quote>tree with two + patches applied</quote>.</para> + + <para id="x_3b8">You can <emphasis>change</emphasis> which patches are + applied to the tree. If you <quote>pop</quote> a patch, the + changes made by that patch will vanish from the directory + tree. Quilt remembers which patches you have popped, though, + so you can <quote>push</quote> a popped patch again, and the + directory tree will be restored to contain the modifications + in the patch. Most importantly, you can run the + <quote>refresh</quote> command at any time, and the topmost + applied patch will be updated. This means that you can, at + any time, change both which patches are applied and what + modifications those patches make.</para> + + <para id="x_3b9">Quilt knows nothing about revision control tools, so it + works equally well on top of an unpacked tarball or a + Subversion working copy.</para> + </sect2> + + <sect2 id="sec:mq:quilt-mq"> + <title>From patchwork quilt to Mercurial Queues</title> + + <para id="x_3ba">In mid-2005, Chris Mason took the features of quilt and + wrote an extension that he called Mercurial Queues, which + added quilt-like behavior to Mercurial.</para> + + <para id="x_3bb">The key difference between quilt and MQ is that quilt + knows nothing about revision control systems, while MQ is + <emphasis>integrated</emphasis> into Mercurial. Each patch + that you push is represented as a Mercurial changeset. Pop a + patch, and the changeset goes away.</para> + + <para id="x_3bc">Because quilt does not care about revision control tools, + it is still a tremendously useful piece of software to know + about for situations where you cannot use Mercurial and + MQ.</para> + + </sect2> + </sect1> + <sect1> + <title>The huge advantage of MQ</title> + + <para id="x_3bd">I cannot overstate the value that MQ offers through the + unification of patches and revision control.</para> + + <para id="x_3be">A major reason that patches have persisted in the free + software and open source world&emdash;in spite of the + availability of increasingly capable revision control tools over + the years&emdash;is the <emphasis>agility</emphasis> they + offer.</para> + + <para id="x_3bf">Traditional revision control tools make a permanent, + irreversible record of everything that you do. While this has + great value, it's also somewhat stifling. If you want to + perform a wild-eyed experiment, you have to be careful in how + you go about it, or you risk leaving unneeded&emdash;or worse, + misleading or destabilising&emdash;traces of your missteps and + errors in the permanent revision record.</para> + + <para id="x_3c0">By contrast, MQ's marriage of distributed revision control + with patches makes it much easier to isolate your work. Your + patches live on top of normal revision history, and you can make + them disappear or reappear at will. If you don't like a patch, + you can drop it. If a patch isn't quite as you want it to be, + simply fix it&emdash;as many times as you need to, until you + have refined it into the form you desire.</para> + + <para id="x_3c1">As an example, the integration of patches with revision + control makes understanding patches and debugging their + effects&emdash;and their interplay with the code they're based + on&emdash;<emphasis>enormously</emphasis> easier. Since every + applied patch has an associated changeset, you can give <command + role="hg-cmd">hg log</command> a file name to see which + changesets and patches affected the file. You can use the + <command role="hg-cmd">hg bisect</command> command to + binary-search through all changesets and applied patches to see + where a bug got introduced or fixed. You can use the <command + role="hg-cmd">hg annotate</command> command to see which + changeset or patch modified a particular line of a source file. + And so on.</para> + </sect1> + + <sect1 id="sec:mq:patch"> + <title>Understanding patches</title> + + <para id="x_3c2">Because MQ doesn't hide its patch-oriented nature, it is + helpful to understand what patches are, and a little about the + tools that work with them.</para> + + <para id="x_3c3">The traditional Unix <command>diff</command> command + compares two files, and prints a list of differences between + them. The <command>patch</command> command understands these + differences as <emphasis>modifications</emphasis> to make to a + file. Take a look below for a simple example of these commands + in action.</para> + + &interaction.mq.dodiff.diff; + + <para id="x_3c4">The type of file that <command>diff</command> generates (and + <command>patch</command> takes as input) is called a + <quote>patch</quote> or a <quote>diff</quote>; there is no + difference between a patch and a diff. (We'll use the term + <quote>patch</quote>, since it's more commonly used.)</para> + + <para id="x_3c5">A patch file can start with arbitrary text; the + <command>patch</command> command ignores this text, but MQ uses + it as the commit message when creating changesets. To find the + beginning of the patch content, <command>patch</command> + searches for the first line that starts with the string + <quote><literal>diff -</literal></quote>.</para> + + <para id="x_3c6">MQ works with <emphasis>unified</emphasis> diffs + (<command>patch</command> can accept several other diff formats, + but MQ doesn't). A unified diff contains two kinds of header. + The <emphasis>file header</emphasis> describes the file being + modified; it contains the name of the file to modify. When + <command>patch</command> sees a new file header, it looks for a + file with that name to start modifying.</para> + + <para id="x_3c7">After the file header comes a series of + <emphasis>hunks</emphasis>. Each hunk starts with a header; + this identifies the range of line numbers within the file that + the hunk should modify. Following the header, a hunk starts and + ends with a few (usually three) lines of text from the + unmodified file; these are called the + <emphasis>context</emphasis> for the hunk. If there's only a + small amount of context between successive hunks, + <command>diff</command> doesn't print a new hunk header; it just + runs the hunks together, with a few lines of context between + modifications.</para> + + <para id="x_3c8">Each line of context begins with a space character. Within + the hunk, a line that begins with + <quote><literal>-</literal></quote> means <quote>remove this + line,</quote> while a line that begins with + <quote><literal>+</literal></quote> means <quote>insert this + line.</quote> For example, a line that is modified is + represented by one deletion and one insertion.</para> + + <para id="x_3c9">We will return to some of the more subtle aspects of patches + later (in <xref linkend="sec:mq:adv-patch"/>), but you + should have + enough information now to use MQ.</para> + </sect1> + + <sect1 id="sec:mq:start"> + <title>Getting started with Mercurial Queues</title> + + <para id="x_3ca">Because MQ is implemented as an extension, you must + explicitly enable before you can use it. (You don't need to + download anything; MQ ships with the standard Mercurial + distribution.) To enable MQ, edit your <filename + role="home">~/.hgrc</filename> file, and add the lines + below.</para> + + <programlisting>[extensions] +hgext.mq =</programlisting> + + <para id="x_3cb">Once the extension is enabled, it will make a number of new + commands available. To verify that the extension is working, + you can use <command role="hg-cmd">hg help</command> to see if + the <command role="hg-ext-mq">qinit</command> command is now + available.</para> + + &interaction.mq.qinit-help.help; + + <para id="x_3cc">You can use MQ with <emphasis>any</emphasis> Mercurial + repository, and its commands only operate within that + repository. To get started, simply prepare the repository using + the <command role="hg-ext-mq">qinit</command> command.</para> + + &interaction.mq.tutorial.qinit; + + <para id="x_3cd">This command creates an empty directory called <filename + role="special" class="directory">.hg/patches</filename>, where + MQ will keep its metadata. As with many Mercurial commands, the + <command role="hg-ext-mq">qinit</command> command prints nothing + if it succeeds.</para> + + <sect2> + <title>Creating a new patch</title> + + <para id="x_3ce">To begin work on a new patch, use the <command + role="hg-ext-mq">qnew</command> command. This command takes + one argument, the name of the patch to create.</para> + + <para id="x_3cf">MQ will use this as the name of an actual file in the + <filename role="special" + class="directory">.hg/patches</filename> directory, as you + can see below.</para> + + &interaction.mq.tutorial.qnew; + + <para id="x_3d0">Also newly present in the <filename role="special" + class="directory">.hg/patches</filename> directory are two + other files, <filename role="special">series</filename> and + <filename role="special">status</filename>. The <filename + role="special">series</filename> file lists all of the + patches that MQ knows about for this repository, with one + patch per line. Mercurial uses the <filename + role="special">status</filename> file for internal + book-keeping; it tracks all of the patches that MQ has + <emphasis>applied</emphasis> in this repository.</para> + + <note> + <para id="x_3d1"> You may sometimes want to edit the <filename + role="special">series</filename> file by hand; for + example, to change the sequence in which some patches are + applied. However, manually editing the <filename + role="special">status</filename> file is almost always a + bad idea, as it's easy to corrupt MQ's idea of what is + happening.</para> + </note> + + <para id="x_3d2">Once you have created your new patch, you can edit files + in the working directory as you usually would. All of the + normal Mercurial commands, such as <command role="hg-cmd">hg + diff</command> and <command role="hg-cmd">hg + annotate</command>, work exactly as they did before.</para> + </sect2> + + <sect2> + <title>Refreshing a patch</title> + + <para id="x_3d3">When you reach a point where you want to save your work, + use the <command role="hg-ext-mq">qrefresh</command> command + to update the patch you are working on.</para> + + &interaction.mq.tutorial.qrefresh; + + <para id="x_3d4">This command folds the changes you have made in the + working directory into your patch, and updates its + corresponding changeset to contain those changes.</para> + + <para id="x_3d5">You can run <command role="hg-ext-mq">qrefresh</command> + as often as you like, so it's a good way to + <quote>checkpoint</quote> your work. Refresh your patch at an + opportune time; try an experiment; and if the experiment + doesn't work out, <command role="hg-cmd">hg revert</command> + your modifications back to the last time you refreshed.</para> + + &interaction.mq.tutorial.qrefresh2; + </sect2> + + <sect2> + <title>Stacking and tracking patches</title> + + <para id="x_3d6">Once you have finished working on a patch, or need to work + on another, you can use the <command + role="hg-ext-mq">qnew</command> command again to create a + new patch. Mercurial will apply this patch on top of your + existing patch.</para> + + &interaction.mq.tutorial.qnew2; + + <para id="x_3d7">Notice that the patch contains the changes in our prior + patch as part of its context (you can see this more clearly in + the output of <command role="hg-cmd">hg + annotate</command>).</para> + + <para id="x_3d8">So far, with the exception of <command + role="hg-ext-mq">qnew</command> and <command + role="hg-ext-mq">qrefresh</command>, we've been careful to + only use regular Mercurial commands. However, MQ provides + many commands that are easier to use when you are thinking + about patches, as illustrated below.</para> + + &interaction.mq.tutorial.qseries; + + <itemizedlist> + <listitem><para id="x_3d9">The <command + role="hg-ext-mq">qseries</command> command lists every + patch that MQ knows about in this repository, from oldest + to newest (most recently + <emphasis>created</emphasis>).</para> + </listitem> + <listitem><para id="x_3da">The <command + role="hg-ext-mq">qapplied</command> command lists every + patch that MQ has <emphasis>applied</emphasis> in this + repository, again from oldest to newest (most recently + applied).</para> + </listitem></itemizedlist> + </sect2> + + <sect2> + <title>Manipulating the patch stack</title> + + <para id="x_3db">The previous discussion implied that there must be a + difference between <quote>known</quote> and + <quote>applied</quote> patches, and there is. MQ can manage a + patch without it being applied in the repository.</para> + + <para id="x_3dc">An <emphasis>applied</emphasis> patch has a corresponding + changeset in the repository, and the effects of the patch and + changeset are visible in the working directory. You can undo + the application of a patch using the <command + role="hg-ext-mq">qpop</command> command. MQ still + <emphasis>knows about</emphasis>, or manages, a popped patch, + but the patch no longer has a corresponding changeset in the + repository, and the working directory does not contain the + changes made by the patch. <xref + linkend="fig:mq:stack"/> illustrates + the difference between applied and tracked patches.</para> + + <figure id="fig:mq:stack"> + <title>Applied and unapplied patches in the MQ patch + stack</title> + <mediaobject> + <imageobject><imagedata fileref="figs/mq-stack.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + </mediaobject> + </figure> + + <para id="x_3de">You can reapply an unapplied, or popped, patch using the + <command role="hg-ext-mq">qpush</command> command. This + creates a new changeset to correspond to the patch, and the + patch's changes once again become present in the working + directory. See below for examples of <command + role="hg-ext-mq">qpop</command> and <command + role="hg-ext-mq">qpush</command> in action.</para> + + &interaction.mq.tutorial.qpop; + + <para id="x_3df">Notice that once we have popped a patch or two patches, + the output of <command role="hg-ext-mq">qseries</command> + remains the same, while that of <command + role="hg-ext-mq">qapplied</command> has changed.</para> + + </sect2> + + <sect2> + <title>Pushing and popping many patches</title> + + <para id="x_3e0">While <command role="hg-ext-mq">qpush</command> and + <command role="hg-ext-mq">qpop</command> each operate on a + single patch at a time by default, you can push and pop many + patches in one go. The <option + role="hg-ext-mq-cmd-qpush-opt">hg -a</option> option to + <command role="hg-ext-mq">qpush</command> causes it to push + all unapplied patches, while the <option + role="hg-ext-mq-cmd-qpop-opt">-a</option> option to <command + role="hg-ext-mq">qpop</command> causes it to pop all applied + patches. (For some more ways to push and pop many patches, + see <xref linkend="sec:mq:perf"/> below.)</para> + + &interaction.mq.tutorial.qpush-a; + </sect2> + + <sect2> + <title>Safety checks, and overriding them</title> + + <para id="x_3e1">Several MQ commands check the working directory before + they do anything, and fail if they find any modifications. + They do this to ensure that you won't lose any changes that + you have made, but not yet incorporated into a patch. The + example below illustrates this; the <command + role="hg-ext-mq">qnew</command> command will not create a + new patch if there are outstanding changes, caused in this + case by the <command role="hg-cmd">hg add</command> of + <filename>file3</filename>.</para> + + &interaction.mq.tutorial.add; + + <para id="x_3e2">Commands that check the working directory all take an + <quote>I know what I'm doing</quote> option, which is always + named <option>-f</option>. The exact meaning of + <option>-f</option> depends on the command. For example, + <command role="hg-cmd">hg qnew <option + role="hg-ext-mq-cmd-qnew-opt">hg -f</option></command> + will incorporate any outstanding changes into the new patch it + creates, but <command role="hg-cmd">hg qpop <option + role="hg-ext-mq-cmd-qpop-opt">hg -f</option></command> + will revert modifications to any files affected by the patch + that it is popping. Be sure to read the documentation for a + command's <option>-f</option> option before you use it!</para> + </sect2> + + <sect2> + <title>Working on several patches at once</title> + + <para id="x_3e3">The <command role="hg-ext-mq">qrefresh</command> command + always refreshes the <emphasis>topmost</emphasis> applied + patch. This means that you can suspend work on one patch (by + refreshing it), pop or push to make a different patch the top, + and work on <emphasis>that</emphasis> patch for a + while.</para> + + <para id="x_3e4">Here's an example that illustrates how you can use this + ability. Let's say you're developing a new feature as two + patches. The first is a change to the core of your software, + and the second&emdash;layered on top of the + first&emdash;changes the user interface to use the code you + just added to the core. If you notice a bug in the core while + you're working on the UI patch, it's easy to fix the core. + Simply <command role="hg-ext-mq">qrefresh</command> the UI + patch to save your in-progress changes, and <command + role="hg-ext-mq">qpop</command> down to the core patch. Fix + the core bug, <command role="hg-ext-mq">qrefresh</command> the + core patch, and <command role="hg-ext-mq">qpush</command> back + to the UI patch to continue where you left off.</para> + </sect2> + </sect1> + + <sect1 id="sec:mq:adv-patch"> + <title>More about patches</title> + + <para id="x_3e5">MQ uses the GNU <command>patch</command> command to apply + patches, so it's helpful to know a few more detailed aspects of + how <command>patch</command> works, and about patches + themselves.</para> + + <sect2> + <title>The strip count</title> + + <para id="x_3e6">If you look at the file headers in a patch, you will + notice that the pathnames usually have an extra component on + the front that isn't present in the actual path name. This is + a holdover from the way that people used to generate patches + (people still do this, but it's somewhat rare with modern + revision control tools).</para> + + <para id="x_3e7">Alice would unpack a tarball, edit her files, then decide + that she wanted to create a patch. So she'd rename her + working directory, unpack the tarball again (hence the need + for the rename), and use the <option + role="cmd-opt-diff">-r</option> and <option + role="cmd-opt-diff">-N</option> options to + <command>diff</command> to recursively generate a patch + between the unmodified directory and the modified one. The + result would be that the name of the unmodified directory + would be at the front of the left-hand path in every file + header, and the name of the modified directory would be at the + front of the right-hand path.</para> + + <para id="x_3e8">Since someone receiving a patch from the Alices of the net + would be unlikely to have unmodified and modified directories + with exactly the same names, the <command>patch</command> + command has a <option role="cmd-opt-patch">-p</option> option + that indicates the number of leading path name components to + strip when trying to apply a patch. This number is called the + <emphasis>strip count</emphasis>.</para> + + <para id="x_3e9">An option of <quote><literal>-p1</literal></quote> means + <quote>use a strip count of one</quote>. If + <command>patch</command> sees a file name + <filename>foo/bar/baz</filename> in a file header, it will + strip <filename>foo</filename> and try to patch a file named + <filename>bar/baz</filename>. (Strictly speaking, the strip + count refers to the number of <emphasis>path + separators</emphasis> (and the components that go with them + ) to strip. A strip count of one will turn + <filename>foo/bar</filename> into <filename>bar</filename>, + but <filename>/foo/bar</filename> (notice the extra leading + slash) into <filename>foo/bar</filename>.)</para> + + <para id="x_3ea">The <quote>standard</quote> strip count for patches is + one; almost all patches contain one leading path name + component that needs to be stripped. Mercurial's <command + role="hg-cmd">hg diff</command> command generates path names + in this form, and the <command role="hg-cmd">hg + import</command> command and MQ expect patches to have a + strip count of one.</para> + + <para id="x_3eb">If you receive a patch from someone that you want to add + to your patch queue, and the patch needs a strip count other + than one, you cannot just <command + role="hg-ext-mq">qimport</command> the patch, because + <command role="hg-ext-mq">qimport</command> does not yet have + a <literal>-p</literal> option (see <ulink role="hg-bug" + url="http://www.selenic.com/mercurial/bts/issue311">issue + 311</ulink>). Your best bet is to <command + role="hg-ext-mq">qnew</command> a patch of your own, then + use <command>patch -pN</command> to apply their patch, + followed by <command role="hg-cmd">hg addremove</command> to + pick up any files added or removed by the patch, followed by + <command role="hg-ext-mq">hg qrefresh</command>. This + complexity may become unnecessary; see <ulink role="hg-bug" + url="http://www.selenic.com/mercurial/bts/issue311">issue + 311</ulink> for details. + </para> + </sect2> + + <sect2> + <title>Strategies for applying a patch</title> + + <para id="x_3ec">When <command>patch</command> applies a hunk, it tries a + handful of successively less accurate strategies to try to + make the hunk apply. This falling-back technique often makes + it possible to take a patch that was generated against an old + version of a file, and apply it against a newer version of + that file.</para> + + <para id="x_3ed">First, <command>patch</command> tries an exact match, + where the line numbers, the context, and the text to be + modified must apply exactly. If it cannot make an exact + match, it tries to find an exact match for the context, + without honouring the line numbering information. If this + succeeds, it prints a line of output saying that the hunk was + applied, but at some <emphasis>offset</emphasis> from the + original line number.</para> + + <para id="x_3ee">If a context-only match fails, <command>patch</command> + removes the first and last lines of the context, and tries a + <emphasis>reduced</emphasis> context-only match. If the hunk + with reduced context succeeds, it prints a message saying that + it applied the hunk with a <emphasis>fuzz factor</emphasis> + (the number after the fuzz factor indicates how many lines of + context <command>patch</command> had to trim before the patch + applied).</para> + + <para id="x_3ef">When neither of these techniques works, + <command>patch</command> prints a message saying that the hunk + in question was rejected. It saves rejected hunks (also + simply called <quote>rejects</quote>) to a file with the same + name, and an added <filename role="special">.rej</filename> + extension. It also saves an unmodified copy of the file with + a <filename role="special">.orig</filename> extension; the + copy of the file without any extensions will contain any + changes made by hunks that <emphasis>did</emphasis> apply + cleanly. If you have a patch that modifies + <filename>foo</filename> with six hunks, and one of them fails + to apply, you will have: an unmodified + <filename>foo.orig</filename>, a <filename>foo.rej</filename> + containing one hunk, and <filename>foo</filename>, containing + the changes made by the five successful hunks.</para> + </sect2> + + <sect2> + <title>Some quirks of patch representation</title> + + <para id="x_3f0">There are a few useful things to know about how + <command>patch</command> works with files.</para> + <itemizedlist> + <listitem><para id="x_3f1">This should already be obvious, but + <command>patch</command> cannot handle binary + files.</para> + </listitem> + <listitem><para id="x_3f2">Neither does it care about the executable bit; + it creates new files as readable, but not + executable.</para> + </listitem> + <listitem><para id="x_3f3"><command>patch</command> treats the removal of + a file as a diff between the file to be removed and the + empty file. So your idea of <quote>I deleted this + file</quote> looks like <quote>every line of this file + was deleted</quote> in a patch.</para> + </listitem> + <listitem><para id="x_3f4">It treats the addition of a file as a diff + between the empty file and the file to be added. So in a + patch, your idea of <quote>I added this file</quote> looks + like <quote>every line of this file was + added</quote>.</para> + </listitem> + <listitem><para id="x_3f5">It treats a renamed file as the removal of the + old name, and the addition of the new name. This means + that renamed files have a big footprint in patches. (Note + also that Mercurial does not currently try to infer when + files have been renamed or copied in a patch.)</para> + </listitem> + <listitem><para id="x_3f6"><command>patch</command> cannot represent + empty files, so you cannot use a patch to represent the + notion <quote>I added this empty file to the + tree</quote>.</para> + </listitem></itemizedlist> + </sect2> + + <sect2> + <title>Beware the fuzz</title> + + <para id="x_3f7">While applying a hunk at an offset, or with a fuzz factor, + will often be completely successful, these inexact techniques + naturally leave open the possibility of corrupting the patched + file. The most common cases typically involve applying a + patch twice, or at an incorrect location in the file. If + <command>patch</command> or <command + role="hg-ext-mq">qpush</command> ever mentions an offset or + fuzz factor, you should make sure that the modified files are + correct afterwards.</para> + + <para id="x_3f8">It's often a good idea to refresh a patch that has applied + with an offset or fuzz factor; refreshing the patch generates + new context information that will make it apply cleanly. I + say <quote>often,</quote> not <quote>always,</quote> because + sometimes refreshing a patch will make it fail to apply + against a different revision of the underlying files. In some + cases, such as when you're maintaining a patch that must sit + on top of multiple versions of a source tree, it's acceptable + to have a patch apply with some fuzz, provided you've verified + the results of the patching process in such cases.</para> + </sect2> + + <sect2> + <title>Handling rejection</title> + + <para id="x_3f9">If <command role="hg-ext-mq">qpush</command> fails to + apply a patch, it will print an error message and exit. If it + has left <filename role="special">.rej</filename> files + behind, it is usually best to fix up the rejected hunks before + you push more patches or do any further work.</para> + + <para id="x_3fa">If your patch <emphasis>used to</emphasis> apply cleanly, + and no longer does because you've changed the underlying code + that your patches are based on, Mercurial Queues can help; see + <xref linkend="sec:mq:merge"/> for details.</para> + + <para id="x_3fb">Unfortunately, there aren't any great techniques for + dealing with rejected hunks. Most often, you'll need to view + the <filename role="special">.rej</filename> file and edit the + target file, applying the rejected hunks by hand.</para> + + <para id="x_3fd">A Linux kernel hacker, Chris Mason (the author + of Mercurial Queues), wrote a tool called + <command>mpatch</command> (<ulink + url="http://oss.oracle.com/~mason/mpatch/">http://oss.oracle.com/~mason/mpatch/</ulink>), + which takes a simple approach to automating the application of + hunks rejected by <command>patch</command>. The + <command>mpatch</command> command can help with four common + reasons that a hunk may be rejected:</para> + + <itemizedlist> + <listitem><para id="x_3fe">The context in the middle of a hunk has + changed.</para> + </listitem> + <listitem><para id="x_3ff">A hunk is missing some context at the + beginning or end.</para> + </listitem> + <listitem><para id="x_400">A large hunk might apply better&emdash;either + entirely or in part&emdash;if it was broken up into + smaller hunks.</para> + </listitem> + <listitem><para id="x_401">A hunk removes lines with slightly different + content than those currently present in the file.</para> + </listitem></itemizedlist> + + <para id="x_402">If you use <command>mpatch</command>, you + should be doubly careful to check your results when you're + done. In fact, <command>mpatch</command> enforces this method + of double-checking the tool's output, by automatically + dropping you into a merge program when it has done its job, so + that you can verify its work and finish off any remaining + merges.</para> + </sect2> + </sect1> + + <sect1> + <title>More on patch management</title> + + <para id="x_6db">As you grow familiar with MQ, you will find yourself wanting + to perform other kinds of patch management operations.</para> + + <sect2> + <title>Deleting unwanted patches</title> + + <para id="x_6dc">If you want to get rid of a patch, use the <command + role="hg-ext-mq">hg qdelete</command> command to delete the + patch file and remove its entry from the patch series. If you + try to delete a patch that is still applied, <command + role="hg-ext-mq">hg qdelete</command> will refuse.</para> + + &interaction.ch11-qdelete.go; + </sect2> + + <sect2> + <title>Converting to and from permanent revisions</title> + + <para id="x_6dd">Once you're done working on a patch and want to + turn it into a permanent changeset, use the <command + role="hg-ext-mq">hg qfinish</command> command. Pass a revision + to the command to identify the patch that you want to turn into + a regular changeset; this patch must already be applied.</para> + + &interaction.ch11-qdelete.convert; + + <para id="x_6e0">The <command role="hg-ext-mq">hg qfinish</command> command + accepts an <option>--all</option> or <option>-a</option> + option, which turns all applied patches into regular + changesets.</para> + + <para id="x_6de">It is also possible to turn an existing changeset into a + patch, by passing the <option>-r</option> option to <command + role="hg-ext-mq">hg qimport</command>.</para> + + &interaction.ch11-qdelete.import; + + <para id="x_6df">Note that it only makes sense to convert a changeset into + a patch if you have not propagated that changeset into any + other repositories. The imported changeset's ID will change + every time you refresh the patch, which will make Mercurial + treat it as unrelated to the original changeset if you have + pushed it somewhere else.</para> + </sect2> + </sect1> + + <sect1 id="sec:mq:perf"> + <title>Getting the best performance out of MQ</title> + + <para id="x_403">MQ is very efficient at handling a large number + of patches. I ran some performance experiments in mid-2006 for a + talk that I gave at the 2006 EuroPython conference (on modern + hardware, you should expect better performance than you'll see + below). I used as my data set the Linux 2.6.17-mm1 patch + series, which consists of 1,738 patches. I applied these on top + of a Linux kernel repository containing all 27,472 revisions + between Linux 2.6.12-rc2 and Linux 2.6.17.</para> + + <para id="x_404">On my old, slow laptop, I was able to <command + role="hg-cmd">hg qpush <option + role="hg-ext-mq-cmd-qpush-opt">hg -a</option></command> all + 1,738 patches in 3.5 minutes, and <command role="hg-cmd">hg qpop + <option role="hg-ext-mq-cmd-qpop-opt">hg -a</option></command> + them all in 30 seconds. (On a newer laptop, the time to push + all patches dropped to two minutes.) I could <command + role="hg-ext-mq">qrefresh</command> one of the biggest patches + (which made 22,779 lines of changes to 287 files) in 6.6 + seconds.</para> + + <para id="x_405">Clearly, MQ is well suited to working in large trees, but + there are a few tricks you can use to get the best performance + of it.</para> + + <para id="x_406">First of all, try to <quote>batch</quote> operations + together. Every time you run <command + role="hg-ext-mq">qpush</command> or <command + role="hg-ext-mq">qpop</command>, these commands scan the + working directory once to make sure you haven't made some + changes and then forgotten to run <command + role="hg-ext-mq">qrefresh</command>. On a small tree, the + time that this scan takes is unnoticeable. However, on a + medium-sized tree (containing tens of thousands of files), it + can take a second or more.</para> + + <para id="x_407">The <command role="hg-ext-mq">qpush</command> and <command + role="hg-ext-mq">qpop</command> commands allow you to push and + pop multiple patches at a time. You can identify the + <quote>destination patch</quote> that you want to end up at. + When you <command role="hg-ext-mq">qpush</command> with a + destination specified, it will push patches until that patch is + at the top of the applied stack. When you <command + role="hg-ext-mq">qpop</command> to a destination, MQ will pop + patches until the destination patch is at the top.</para> + + <para id="x_408">You can identify a destination patch using either the name + of the patch, or by number. If you use numeric addressing, + patches are counted from zero; this means that the first patch + is zero, the second is one, and so on.</para> + </sect1> + + <sect1 id="sec:mq:merge"> + <title>Updating your patches when the underlying code + changes</title> + + <para id="x_409">It's common to have a stack of patches on top of an + underlying repository that you don't modify directly. If you're + working on changes to third-party code, or on a feature that is + taking longer to develop than the rate of change of the code + beneath, you will often need to sync up with the underlying + code, and fix up any hunks in your patches that no longer apply. + This is called <emphasis>rebasing</emphasis> your patch + series.</para> + + <para id="x_40a">The simplest way to do this is to <command role="hg-cmd">hg + qpop <option role="hg-ext-mq-cmd-qpop-opt">hg + -a</option></command> your patches, then <command + role="hg-cmd">hg pull</command> changes into the underlying + repository, and finally <command role="hg-cmd">hg qpush <option + role="hg-ext-mq-cmd-qpop-opt">hg -a</option></command> your + patches again. MQ will stop pushing any time it runs across a + patch that fails to apply during conflicts, allowing you to fix + your conflicts, <command role="hg-ext-mq">qrefresh</command> the + affected patch, and continue pushing until you have fixed your + entire stack.</para> + + <para id="x_40b">This approach is easy to use and works well if you don't + expect changes to the underlying code to affect how well your + patches apply. If your patch stack touches code that is modified + frequently or invasively in the underlying repository, however, + fixing up rejected hunks by hand quickly becomes + tiresome.</para> + + <para id="x_40c">It's possible to partially automate the rebasing process. + If your patches apply cleanly against some revision of the + underlying repo, MQ can use this information to help you to + resolve conflicts between your patches and a different + revision.</para> + + <para id="x_40d">The process is a little involved.</para> + <orderedlist> + <listitem><para id="x_40e">To begin, <command role="hg-cmd">hg qpush + -a</command> all of your patches on top of the revision + where you know that they apply cleanly.</para> + </listitem> + <listitem><para id="x_40f">Save a backup copy of your patch directory using + <command role="hg-cmd">hg qsave <option + role="hg-ext-mq-cmd-qsave-opt">hg -e</option> <option + role="hg-ext-mq-cmd-qsave-opt">hg -c</option></command>. + This prints the name of the directory that it has saved the + patches in. It will save the patches to a directory called + <filename role="special" + class="directory">.hg/patches.N</filename>, where + <literal>N</literal> is a small integer. It also commits a + <quote>save changeset</quote> on top of your applied + patches; this is for internal book-keeping, and records the + states of the <filename role="special">series</filename> and + <filename role="special">status</filename> files.</para> + </listitem> + <listitem><para id="x_410">Use <command role="hg-cmd">hg pull</command> to + bring new changes into the underlying repository. (Don't + run <command role="hg-cmd">hg pull -u</command>; see below + for why.)</para> + </listitem> + <listitem><para id="x_411">Update to the new tip revision, using <command + role="hg-cmd">hg update <option + role="hg-opt-update">-C</option></command> to override + the patches you have pushed.</para> + </listitem> + <listitem><para id="x_412">Merge all patches using <command>hg qpush -m + -a</command>. The <option + role="hg-ext-mq-cmd-qpush-opt">-m</option> option to + <command role="hg-ext-mq">qpush</command> tells MQ to + perform a three-way merge if the patch fails to + apply.</para> + </listitem></orderedlist> + + <para id="x_413">During the <command role="hg-cmd">hg qpush <option + role="hg-ext-mq-cmd-qpush-opt">hg -m</option></command>, + each patch in the <filename role="special">series</filename> + file is applied normally. If a patch applies with fuzz or + rejects, MQ looks at the queue you <command + role="hg-ext-mq">qsave</command>d, and performs a three-way + merge with the corresponding changeset. This merge uses + Mercurial's normal merge machinery, so it may pop up a GUI merge + tool to help you to resolve problems.</para> + + <para id="x_414">When you finish resolving the effects of a patch, MQ + refreshes your patch based on the result of the merge.</para> + + <para id="x_415">At the end of this process, your repository will have one + extra head from the old patch queue, and a copy of the old patch + queue will be in <filename role="special" + class="directory">.hg/patches.N</filename>. You can remove the + extra head using <command role="hg-cmd">hg qpop -a -n + patches.N</command> or <command role="hg-cmd">hg + strip</command>. You can delete <filename role="special" + class="directory">.hg/patches.N</filename> once you are sure + that you no longer need it as a backup.</para> + </sect1> + + <sect1> + <title>Identifying patches</title> + + <para id="x_416">MQ commands that work with patches let you refer to a patch + either by using its name or by a number. By name is obvious + enough; pass the name <filename>foo.patch</filename> to <command + role="hg-ext-mq">qpush</command>, for example, and it will + push patches until <filename>foo.patch</filename> is + applied.</para> + + <para id="x_417">As a shortcut, you can refer to a patch using both a name + and a numeric offset; <literal>foo.patch-2</literal> means + <quote>two patches before <literal>foo.patch</literal></quote>, + while <literal>bar.patch+4</literal> means <quote>four patches + after <literal>bar.patch</literal></quote>.</para> + + <para id="x_418">Referring to a patch by index isn't much different. The + first patch printed in the output of <command + role="hg-ext-mq">qseries</command> is patch zero (yes, it's + one of those start-at-zero counting systems); the second is + patch one; and so on.</para> + + <para id="x_419">MQ also makes it easy to work with patches when you are + using normal Mercurial commands. Every command that accepts a + changeset ID will also accept the name of an applied patch. MQ + augments the tags normally in the repository with an eponymous + one for each applied patch. In addition, the special tags + <literal role="tag">qbase</literal> and + <literal role="tag">qtip</literal> identify + the <quote>bottom-most</quote> and topmost applied patches, + respectively.</para> + + <para id="x_41a">These additions to Mercurial's normal tagging capabilities + make dealing with patches even more of a breeze.</para> + <itemizedlist> + <listitem><para id="x_41b">Want to patchbomb a mailing list with your + latest series of changes?</para> + <programlisting>hg email qbase:qtip</programlisting> + <para id="x_41c"> (Don't know what <quote>patchbombing</quote> is? See + <xref linkend="sec:hgext:patchbomb"/>.)</para> + </listitem> + <listitem><para id="x_41d">Need to see all of the patches since + <literal>foo.patch</literal> that have touched files in a + subdirectory of your tree?</para> + <programlisting>hg log -r foo.patch:qtip subdir</programlisting> + </listitem> + </itemizedlist> + + <para id="x_41e">Because MQ makes the names of patches available to the rest + of Mercurial through its normal internal tag machinery, you + don't need to type in the entire name of a patch when you want + to identify it by name.</para> + + <para id="x_41f">Another nice consequence of representing patch names as tags + is that when you run the <command role="hg-cmd">hg log</command> + command, it will display a patch's name as a tag, simply as part + of its normal output. This makes it easy to visually + distinguish applied patches from underlying + <quote>normal</quote> revisions. The following example shows a + few normal Mercurial commands in use with applied + patches.</para> + + &interaction.mq.id.output; + </sect1> + + <sect1> + <title>Useful things to know about</title> + + <para id="x_420">There are a number of aspects of MQ usage that don't fit + tidily into sections of their own, but that are good to know. + Here they are, in one place.</para> + + <itemizedlist> + <listitem><para id="x_421">Normally, when you <command + role="hg-ext-mq">qpop</command> a patch and <command + role="hg-ext-mq">qpush</command> it again, the changeset + that represents the patch after the pop/push will have a + <emphasis>different identity</emphasis> than the changeset + that represented the hash beforehand. See <xref + linkend="sec:mqref:cmd:qpush"/> for + information as to why this is.</para> + </listitem> + <listitem><para id="x_422">It's not a good idea to <command + role="hg-cmd">hg merge</command> changes from another + branch with a patch changeset, at least if you want to + maintain the <quote>patchiness</quote> of that changeset and + changesets below it on the patch stack. If you try to do + this, it will appear to succeed, but MQ will become + confused.</para> + </listitem></itemizedlist> + </sect1> + + <sect1 id="sec:mq:repo"> + <title>Managing patches in a repository</title> + + <para id="x_423">Because MQ's <filename role="special" + class="directory">.hg/patches</filename> directory resides + outside a Mercurial repository's working directory, the + <quote>underlying</quote> Mercurial repository knows nothing + about the management or presence of patches.</para> + + <para id="x_424">This presents the interesting possibility of managing the + contents of the patch directory as a Mercurial repository in its + own right. This can be a useful way to work. For example, you + can work on a patch for a while, <command + role="hg-ext-mq">qrefresh</command> it, then <command + role="hg-cmd">hg commit</command> the current state of the + patch. This lets you <quote>roll back</quote> to that version + of the patch later on.</para> + + <para id="x_425">You can then share different versions of the same patch + stack among multiple underlying repositories. I use this when I + am developing a Linux kernel feature. I have a pristine copy of + my kernel sources for each of several CPU architectures, and a + cloned repository under each that contains the patches I am + working on. When I want to test a change on a different + architecture, I push my current patches to the patch repository + associated with that kernel tree, pop and push all of my + patches, and build and test that kernel.</para> + + <para id="x_426">Managing patches in a repository makes it possible for + multiple developers to work on the same patch series without + colliding with each other, all on top of an underlying source + base that they may or may not control.</para> + + <sect2> + <title>MQ support for patch repositories</title> + + <para id="x_427">MQ helps you to work with the <filename role="special" + class="directory">.hg/patches</filename> directory as a + repository; when you prepare a repository for working with + patches using <command role="hg-ext-mq">qinit</command>, you + can pass the <option role="hg-ext-mq-cmd-qinit-opt">hg + -c</option> option to create the <filename role="special" + class="directory">.hg/patches</filename> directory as a + Mercurial repository.</para> + + <note> + <para id="x_428"> If you forget to use the <option + role="hg-ext-mq-cmd-qinit-opt">hg -c</option> option, you + can simply go into the <filename role="special" + class="directory">.hg/patches</filename> directory at any + time and run <command role="hg-cmd">hg init</command>. + Don't forget to add an entry for the <filename + role="special">status</filename> file to the <filename + role="special">.hgignore</filename> file, though</para> + + <para id="x_429"> (<command role="hg-cmd">hg qinit <option + role="hg-ext-mq-cmd-qinit-opt">hg -c</option></command> + does this for you automatically); you + <emphasis>really</emphasis> don't want to manage the + <filename role="special">status</filename> file.</para> + </note> + + <para id="x_42a">As a convenience, if MQ notices that the <filename + class="directory">.hg/patches</filename> directory is a + repository, it will automatically <command role="hg-cmd">hg + add</command> every patch that you create and import.</para> + + <para id="x_42b">MQ provides a shortcut command, <command + role="hg-ext-mq">qcommit</command>, that runs <command + role="hg-cmd">hg commit</command> in the <filename + role="special" class="directory">.hg/patches</filename> + directory. This saves some bothersome typing.</para> + + <para id="x_42c">Finally, as a convenience to manage the patch directory, + you can define the alias <command>mq</command> on Unix + systems. For example, on Linux systems using the + <command>bash</command> shell, you can include the following + snippet in your <filename + role="home">~/.bashrc</filename>.</para> + + <programlisting>alias mq=`hg -R $(hg root)/.hg/patches'</programlisting> + + <para id="x_42d">You can then issue commands of the form <command>mq + pull</command> from the main repository.</para> + </sect2> + + <sect2> + <title>A few things to watch out for</title> + + <para id="x_42e">MQ's support for working with a repository full of patches + is limited in a few small respects.</para> + + <para id="x_42f">MQ cannot automatically detect changes that you make to + the patch directory. If you <command role="hg-cmd">hg + pull</command>, manually edit, or <command role="hg-cmd">hg + update</command> changes to patches or the <filename + role="special">series</filename> file, you will have to + <command role="hg-cmd">hg qpop <option + role="hg-ext-mq-cmd-qpop-opt">hg -a</option></command> and + then <command role="hg-cmd">hg qpush <option + role="hg-ext-mq-cmd-qpush-opt">hg -a</option></command> in + the underlying repository to see those changes show up there. + If you forget to do this, you can confuse MQ's idea of which + patches are applied.</para> + + </sect2> + </sect1> + <sect1 id="sec:mq:tools"> + <title>Third party tools for working with patches</title> + + <para id="x_430">Once you've been working with patches for a while, you'll + find yourself hungry for tools that will help you to understand + and manipulate the patches you're dealing with.</para> + + <para id="x_431">The <command>diffstat</command> command + <citation>web:diffstat</citation> generates a histogram of the + modifications made to each file in a patch. It provides a good + way to <quote>get a sense of</quote> a patch&emdash;which files + it affects, and how much change it introduces to each file and + as a whole. (I find that it's a good idea to use + <command>diffstat</command>'s <option + role="cmd-opt-diffstat">-p</option> option as a matter of + course, as otherwise it will try to do clever things with + prefixes of file names that inevitably confuse at least + me.)</para> + +&interaction.mq.tools.tools; + + <para id="x_432">The <literal role="package">patchutils</literal> package + <citation>web:patchutils</citation> is invaluable. It provides a + set of small utilities that follow the <quote>Unix + philosophy;</quote> each does one useful thing with a patch. + The <literal role="package">patchutils</literal> command I use + most is <command>filterdiff</command>, which extracts subsets + from a patch file. For example, given a patch that modifies + hundreds of files across dozens of directories, a single + invocation of <command>filterdiff</command> can generate a + smaller patch that only touches files whose names match a + particular glob pattern. See <xref + linkend="mq-collab:tips:interdiff"/> for another + example.</para> + + </sect1> + <sect1> + <title>Good ways to work with patches</title> + + <para id="x_433">Whether you are working on a patch series to submit to a + free software or open source project, or a series that you + intend to treat as a sequence of regular changesets when you're + done, you can use some simple techniques to keep your work well + organized.</para> + + <para id="x_434">Give your patches descriptive names. A good name for a + patch might be <filename>rework-device-alloc.patch</filename>, + because it will immediately give you a hint what the purpose of + the patch is. Long names shouldn't be a problem; you won't be + typing the names often, but you <emphasis>will</emphasis> be + running commands like <command + role="hg-ext-mq">qapplied</command> and <command + role="hg-ext-mq">qtop</command> over and over. Good naming + becomes especially important when you have a number of patches + to work with, or if you are juggling a number of different tasks + and your patches only get a fraction of your attention.</para> + + <para id="x_435">Be aware of what patch you're working on. Use the <command + role="hg-ext-mq">qtop</command> command and skim over the text + of your patches frequently&emdash;for example, using <command + role="hg-cmd">hg tip <option + role="hg-opt-tip">-p</option></command>)&emdash;to be sure + of where you stand. I have several times worked on and <command + role="hg-ext-mq">qrefresh</command>ed a patch other than the + one I intended, and it's often tricky to migrate changes into + the right patch after making them in the wrong one.</para> + + <para id="x_436">For this reason, it is very much worth investing a little + time to learn how to use some of the third-party tools I + described in <xref linkend="sec:mq:tools"/>, + particularly + <command>diffstat</command> and <command>filterdiff</command>. + The former will give you a quick idea of what changes your patch + is making, while the latter makes it easy to splice hunks + selectively out of one patch and into another.</para> + + </sect1> + <sect1> + <title>MQ cookbook</title> + + <sect2> + <title>Manage <quote>trivial</quote> patches</title> + + <para id="x_437">Because the overhead of dropping files into a new + Mercurial repository is so low, it makes a lot of sense to + manage patches this way even if you simply want to make a few + changes to a source tarball that you downloaded.</para> + + <para id="x_438">Begin by downloading and unpacking the source tarball, and + turning it into a Mercurial repository.</para> + + &interaction.mq.tarball.download; + + <para id="x_439">Continue by creating a patch stack and making your + changes.</para> + + &interaction.mq.tarball.qinit; + + <para id="x_43a">Let's say a few weeks or months pass, and your package + author releases a new version. First, bring their changes + into the repository.</para> + + &interaction.mq.tarball.newsource; + + <para id="x_43b">The pipeline starting with <command role="hg-cmd">hg + locate</command> above deletes all files in the working + directory, so that <command role="hg-cmd">hg + commit</command>'s <option + role="hg-opt-commit">--addremove</option> option can + actually tell which files have really been removed in the + newer version of the source.</para> + + <para id="x_43c">Finally, you can apply your patches on top of the new + tree.</para> + + &interaction.mq.tarball.repush; + </sect2> + + <sect2 id="sec:mq:combine"> + <title>Combining entire patches</title> + + <para id="x_43d">MQ provides a command, <command + role="hg-ext-mq">qfold</command> that lets you combine + entire patches. This <quote>folds</quote> the patches you + name, in the order you name them, into the topmost applied + patch, and concatenates their descriptions onto the end of its + description. The patches that you fold must be unapplied + before you fold them.</para> + + <para id="x_43e">The order in which you fold patches matters. If your + topmost applied patch is <literal>foo</literal>, and you + <command role="hg-ext-mq">qfold</command> + <literal>bar</literal> and <literal>quux</literal> into it, + you will end up with a patch that has the same effect as if + you applied first <literal>foo</literal>, then + <literal>bar</literal>, followed by + <literal>quux</literal>.</para> + </sect2> + + <sect2> + <title>Merging part of one patch into another</title> + + <para id="x_43f">Merging <emphasis>part</emphasis> of one patch into + another is more difficult than combining entire + patches.</para> + + <para id="x_440">If you want to move changes to entire files, you can use + <command>filterdiff</command>'s <option + role="cmd-opt-filterdiff">-i</option> and <option + role="cmd-opt-filterdiff">-x</option> options to choose the + modifications to snip out of one patch, concatenating its + output onto the end of the patch you want to merge into. You + usually won't need to modify the patch you've merged the + changes from. Instead, MQ will report some rejected hunks + when you <command role="hg-ext-mq">qpush</command> it (from + the hunks you moved into the other patch), and you can simply + <command role="hg-ext-mq">qrefresh</command> the patch to drop + the duplicate hunks.</para> + + <para id="x_441">If you have a patch that has multiple hunks modifying a + file, and you only want to move a few of those hunks, the job + becomes more messy, but you can still partly automate it. Use + <command>lsdiff -nvv</command> to print some metadata about + the patch.</para> + + &interaction.mq.tools.lsdiff; + + <para id="x_442">This command prints three different kinds of + number:</para> + <itemizedlist> + <listitem><para id="x_443">(in the first column) a <emphasis>file + number</emphasis> to identify each file modified in the + patch;</para> + </listitem> + <listitem><para id="x_444">(on the next line, indented) the line number + within a modified file where a hunk starts; and</para> + </listitem> + <listitem><para id="x_445">(on the same line) a <emphasis>hunk + number</emphasis> to identify that hunk.</para> + </listitem></itemizedlist> + + <para id="x_446">You'll have to use some visual inspection, and reading of + the patch, to identify the file and hunk numbers you'll want, + but you can then pass them to to + <command>filterdiff</command>'s <option + role="cmd-opt-filterdiff">--files</option> and <option + role="cmd-opt-filterdiff">--hunks</option> options, to + select exactly the file and hunk you want to extract.</para> + + <para id="x_447">Once you have this hunk, you can concatenate it onto the + end of your destination patch and continue with the remainder + of <xref linkend="sec:mq:combine"/>.</para> + + </sect2> + </sect1> + <sect1> + <title>Differences between quilt and MQ</title> + + <para id="x_448">If you are already familiar with quilt, MQ provides a + similar command set. There are a few differences in the way + that it works.</para> + + <para id="x_449">You will already have noticed that most quilt commands have + MQ counterparts that simply begin with a + <quote><literal>q</literal></quote>. The exceptions are quilt's + <literal>add</literal> and <literal>remove</literal> commands, + the counterparts for which are the normal Mercurial <command + role="hg-cmd">hg add</command> and <command role="hg-cmd">hg + remove</command> commands. There is no MQ equivalent of the + quilt <literal>edit</literal> command.</para> + + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch13-mq-collab.xml --- a/fr/ch13-mq-collab.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch13-mq-collab.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,499 +1,525 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Advanced uses of Mercurial Queues</title> -<para>\label{chap:mq-collab}</para> - -<para>While it's easy to pick up straightforward uses of Mercurial Queues, -use of a little discipline and some of MQ's less frequently used -capabilities makes it possible to work in complicated development -environments.</para> - -<para>In this chapter, I will use as an example a technique I have used to -manage the development of an Infiniband device driver for the Linux -kernel. The driver in question is large (at least as drivers go), -with 25,000 lines of code spread across 35 source files. It is -maintained by a small team of developers.</para> - -<para>While much of the material in this chapter is specific to Linux, the -same principles apply to any code base for which you're not the -primary owner, and upon which you need to do a lot of development.</para> - -<sect1> -<title>The problem of many targets</title> - -<para>The Linux kernel changes rapidly, and has never been internally -stable; developers frequently make drastic changes between releases. -This means that a version of the driver that works well with a -particular released version of the kernel will not even <emphasis>compile</emphasis> -correctly against, typically, any other version.</para> - -<para>To maintain a driver, we have to keep a number of distinct versions of -Linux in mind.</para> -<itemizedlist> -<listitem><para>One target is the main Linux kernel development tree. - Maintenance of the code is in this case partly shared by other - developers in the kernel community, who make <quote>drive-by</quote> - modifications to the driver as they develop and refine kernel - subsystems.</para> -</listitem> -<listitem><para>We also maintain a number of <quote>backports</quote> to older versions of - the Linux kernel, to support the needs of customers who are running - older Linux distributions that do not incorporate our drivers. (To - <emphasis>backport</emphasis> a piece of code is to modify it to work in an older - version of its target environment than the version it was developed - for.)</para> -</listitem> -<listitem><para>Finally, we make software releases on a schedule that is - necessarily not aligned with those used by Linux distributors and - kernel developers, so that we can deliver new features to customers - without forcing them to upgrade their entire kernels or - distributions. -</para> -</listitem></itemizedlist> - -<sect2> -<title>Tempting approaches that don't work well</title> - -<para>There are two <quote>standard</quote> ways to maintain a piece of software that -has to target many different environments. -</para> - -<para>The first is to maintain a number of branches, each intended for a -single target. The trouble with this approach is that you must -maintain iron discipline in the flow of changes between repositories. -A new feature or bug fix must start life in a <quote>pristine</quote> repository, -then percolate out to every backport repository. Backport changes are -more limited in the branches they should propagate to; a backport -change that is applied to a branch where it doesn't belong will -probably stop the driver from compiling. -</para> - -<para>The second is to maintain a single source tree filled with conditional -statements that turn chunks of code on or off depending on the -intended target. Because these <quote>ifdefs</quote> are not allowed in the -Linux kernel tree, a manual or automatic process must be followed to -strip them out and yield a clean tree. A code base maintained in this -fashion rapidly becomes a rat's nest of conditional blocks that are -difficult to understand and maintain. -</para> - -<para>Neither of these approaches is well suited to a situation where you -don't <quote>own</quote> the canonical copy of a source tree. In the case of a -Linux driver that is distributed with the standard kernel, Linus's -tree contains the copy of the code that will be treated by the world -as canonical. The upstream version of <quote>my</quote> driver can be modified -by people I don't know, without me even finding out about it until -after the changes show up in Linus's tree. -</para> - -<para>These approaches have the added weakness of making it difficult to -generate well-formed patches to submit upstream. -</para> - -<para>In principle, Mercurial Queues seems like a good candidate to manage a -development scenario such as the above. While this is indeed the -case, MQ contains a few added features that make the job more -pleasant. -</para> - -<para>\section{Conditionally applying patches with - guards} -</para> - -<para>Perhaps the best way to maintain sanity with so many targets is to be -able to choose specific patches to apply for a given situation. MQ -provides a feature called <quote>guards</quote> (which originates with quilt's -<literal>guards</literal> command) that does just this. To start off, let's -create a simple repository for experimenting in. -<!-- &interaction.mq.guards.init; --> -This gives us a tiny repository that contains two patches that don't -have any dependencies on each other, because they touch different files. -</para> - -<para>The idea behind conditional application is that you can <quote>tag</quote> a -patch with a <emphasis>guard</emphasis>, which is simply a text string of your -choosing, then tell MQ to select specific guards to use when applying -patches. MQ will then either apply, or skip over, a guarded patch, -depending on the guards that you have selected. -</para> - -<para>A patch can have an arbitrary number of guards; -each one is <emphasis>positive</emphasis> (<quote>apply this patch if this guard is -selected</quote>) or <emphasis>negative</emphasis> (<quote>skip this patch if this guard is -selected</quote>). A patch with no guards is always applied. -</para> - -</sect2> -</sect1> -<sect1> -<title>Controlling the guards on a patch</title> - -<para>The <command role="hg-ext-mq">qguard</command> command lets you determine which guards should -apply to a patch, or display the guards that are already in effect. -Without any arguments, it displays the guards on the current topmost -patch. -<!-- &interaction.mq.guards.qguard; --> -To set a positive guard on a patch, prefix the name of the guard with -a <quote><literal>+</literal></quote>. -<!-- &interaction.mq.guards.qguard.pos; --> -To set a negative guard on a patch, prefix the name of the guard with -a <quote><literal>-</literal></quote>. -<!-- &interaction.mq.guards.qguard.neg; --> -</para> - -<note> -<para> The <command role="hg-ext-mq">qguard</command> command <emphasis>sets</emphasis> the guards on a patch; it - doesn't <emphasis>modify</emphasis> them. What this means is that if you run - <command role="hg-cmd">hg qguard +a +b</command> on a patch, then <command role="hg-cmd">hg qguard +c</command> on - the same patch, the <emphasis>only</emphasis> guard that will be set on it - afterwards is <literal>+c</literal>. -</para> -</note> - -<para>Mercurial stores guards in the <filename role="special">series</filename> file; the form in -which they are stored is easy both to understand and to edit by hand. -(In other words, you don't have to use the <command role="hg-ext-mq">qguard</command> command if -you don't want to; it's okay to simply edit the <filename role="special">series</filename> -file.) -<!-- &interaction.mq.guards.series; --> -</para> - -</sect1> -<sect1> -<title>Selecting the guards to use</title> - -<para>The <command role="hg-ext-mq">qselect</command> command determines which guards are active at a -given time. The effect of this is to determine which patches MQ will -apply the next time you run <command role="hg-ext-mq">qpush</command>. It has no other effect; in -particular, it doesn't do anything to patches that are already -applied. -</para> - -<para>With no arguments, the <command role="hg-ext-mq">qselect</command> command lists the guards -currently in effect, one per line of output. Each argument is treated -as the name of a guard to apply. -<!-- &interaction.mq.guards.qselect.foo; --> -In case you're interested, the currently selected guards are stored in -the <filename role="special">guards</filename> file. -<!-- &interaction.mq.guards.qselect.cat; --> -We can see the effect the selected guards have when we run -<command role="hg-ext-mq">qpush</command>. -<!-- &interaction.mq.guards.qselect.qpush; --> -</para> - -<para>A guard cannot start with a <quote><literal>+</literal></quote> or <quote><literal>-</literal></quote> -character. The name of a guard must not contain white space, but most -other characters are acceptable. If you try to use a guard with an -invalid name, MQ will complain: -<!-- &interaction.mq.guards.qselect.error; --> -Changing the selected guards changes the patches that are applied. -<!-- &interaction.mq.guards.qselect.quux; --> -You can see in the example below that negative guards take precedence -over positive guards. -<!-- &interaction.mq.guards.qselect.foobar; --> -</para> - -</sect1> -<sect1> -<title>MQ's rules for applying patches</title> - -<para>The rules that MQ uses when deciding whether to apply a patch -are as follows. -</para> -<itemizedlist> -<listitem><para>A patch that has no guards is always applied. -</para> -</listitem> -<listitem><para>If the patch has any negative guard that matches any currently - selected guard, the patch is skipped. -</para> -</listitem> -<listitem><para>If the patch has any positive guard that matches any currently - selected guard, the patch is applied. -</para> -</listitem> -<listitem><para>If the patch has positive or negative guards, but none matches - any currently selected guard, the patch is skipped. -</para> -</listitem></itemizedlist> - -</sect1> -<sect1> -<title>Trimming the work environment</title> - -<para>In working on the device driver I mentioned earlier, I don't apply the -patches to a normal Linux kernel tree. Instead, I use a repository -that contains only a snapshot of the source files and headers that are -relevant to Infiniband development. This repository is 1% the size -of a kernel repository, so it's easier to work with. -</para> - -<para>I then choose a <quote>base</quote> version on top of which the patches are -applied. This is a snapshot of the Linux kernel tree as of a revision -of my choosing. When I take the snapshot, I record the changeset ID -from the kernel repository in the commit message. Since the snapshot -preserves the <quote>shape</quote> and content of the relevant parts of the -kernel tree, I can apply my patches on top of either my tiny -repository or a normal kernel tree. -</para> - -<para>Normally, the base tree atop which the patches apply should be a -snapshot of a very recent upstream tree. This best facilitates the -development of patches that can easily be submitted upstream with few -or no modifications. -</para> - -</sect1> -<sect1> -<title>Dividing up the <filename role="special">series</filename> file</title> - -<para>I categorise the patches in the <filename role="special">series</filename> file into a number -of logical groups. Each section of like patches begins with a block -of comments that describes the purpose of the patches that follow. -</para> - -<para>The sequence of patch groups that I maintain follows. The ordering of -these groups is important; I'll describe why after I introduce the -groups. -</para> -<itemizedlist> -<listitem><para>The <quote>accepted</quote> group. Patches that the development team has - submitted to the maintainer of the Infiniband subsystem, and which - he has accepted, but which are not present in the snapshot that the - tiny repository is based on. These are <quote>read only</quote> patches, - present only to transform the tree into a similar state as it is in - the upstream maintainer's repository. -</para> -</listitem> -<listitem><para>The <quote>rework</quote> group. Patches that I have submitted, but that - the upstream maintainer has requested modifications to before he - will accept them. -</para> -</listitem> -<listitem><para>The <quote>pending</quote> group. Patches that I have not yet submitted to - the upstream maintainer, but which we have finished working on. - These will be <quote>read only</quote> for a while. If the upstream maintainer - accepts them upon submission, I'll move them to the end of the - <quote>accepted</quote> group. If he requests that I modify any, I'll move - them to the beginning of the <quote>rework</quote> group. -</para> -</listitem> -<listitem><para>The <quote>in progress</quote> group. Patches that are actively being - developed, and should not be submitted anywhere yet. -</para> -</listitem> -<listitem><para>The <quote>backport</quote> group. Patches that adapt the source tree to - older versions of the kernel tree. -</para> -</listitem> -<listitem><para>The <quote>do not ship</quote> group. Patches that for some reason should - never be submitted upstream. For example, one such patch might - change embedded driver identification strings to make it easier to - distinguish, in the field, between an out-of-tree version of the - driver and a version shipped by a distribution vendor. -</para> -</listitem></itemizedlist> - -<para>Now to return to the reasons for ordering groups of patches in this -way. We would like the lowest patches in the stack to be as stable as -possible, so that we will not need to rework higher patches due to -changes in context. Putting patches that will never be changed first -in the <filename role="special">series</filename> file serves this purpose. -</para> - -<para>We would also like the patches that we know we'll need to modify to be -applied on top of a source tree that resembles the upstream tree as -closely as possible. This is why we keep accepted patches around for -a while. -</para> - -<para>The <quote>backport</quote> and <quote>do not ship</quote> patches float at the end of the -<filename role="special">series</filename> file. The backport patches must be applied on top -of all other patches, and the <quote>do not ship</quote> patches might as well -stay out of harm's way. -</para> - -</sect1> -<sect1> -<title>Maintaining the patch series</title> - -<para>In my work, I use a number of guards to control which patches are to -be applied. -</para> - -<itemizedlist> -<listitem><para><quote>Accepted</quote> patches are guarded with <literal>accepted</literal>. I - enable this guard most of the time. When I'm applying the patches - on top of a tree where the patches are already present, I can turn - this patch off, and the patches that follow it will apply cleanly. -</para> -</listitem> -<listitem><para>Patches that are <quote>finished</quote>, but not yet submitted, have no - guards. If I'm applying the patch stack to a copy of the upstream - tree, I don't need to enable any guards in order to get a reasonably - safe source tree. -</para> -</listitem> -<listitem><para>Those patches that need reworking before being resubmitted are - guarded with <literal>rework</literal>. -</para> -</listitem> -<listitem><para>For those patches that are still under development, I use - <literal>devel</literal>. -</para> -</listitem> -<listitem><para>A backport patch may have several guards, one for each version - of the kernel to which it applies. For example, a patch that - backports a piece of code to 2.6.9 will have a <literal>2.6.9</literal> guard. -</para> -</listitem></itemizedlist> -<para>This variety of guards gives me considerable flexibility in -determining what kind of source tree I want to end up with. For most -situations, the selection of appropriate guards is automated during -the build process, but I can manually tune the guards to use for less -common circumstances. -</para> - -<sect2> -<title>The art of writing backport patches</title> - -<para>Using MQ, writing a backport patch is a simple process. All such a -patch has to do is modify a piece of code that uses a kernel feature -not present in the older version of the kernel, so that the driver -continues to work correctly under that older version. -</para> - -<para>A useful goal when writing a good backport patch is to make your code -look as if it was written for the older version of the kernel you're -targeting. The less obtrusive the patch, the easier it will be to -understand and maintain. If you're writing a collection of backport -patches to avoid the <quote>rat's nest</quote> effect of lots of -<literal>#ifdef</literal>s (hunks of source code that are only used -conditionally) in your code, don't introduce version-dependent -<literal>#ifdef</literal>s into the patches. Instead, write several patches, -each of which makes unconditional changes, and control their -application using guards. -</para> - -<para>There are two reasons to divide backport patches into a distinct -group, away from the <quote>regular</quote> patches whose effects they modify. -The first is that intermingling the two makes it more difficult to use -a tool like the <literal role="hg-ext">patchbomb</literal> extension to automate the process of -submitting the patches to an upstream maintainer. The second is that -a backport patch could perturb the context in which a subsequent -regular patch is applied, making it impossible to apply the regular -patch cleanly <emphasis>without</emphasis> the earlier backport patch already being -applied. -</para> - -</sect2> -</sect1> -<sect1> -<title>Useful tips for developing with MQ</title> - -<sect2> -<title>Organising patches in directories</title> - -<para>If you're working on a substantial project with MQ, it's not difficult -to accumulate a large number of patches. For example, I have one -patch repository that contains over 250 patches. -</para> - -<para>If you can group these patches into separate logical categories, you -can if you like store them in different directories; MQ has no -problems with patch names that contain path separators. -</para> - -</sect2> -<sect2> -<title>Viewing the history of a patch</title> -<para>\label{mq-collab:tips:interdiff} -</para> - -<para>If you're developing a set of patches over a long time, it's a good -idea to maintain them in a repository, as discussed in -section <xref linkend="sec:mq:repo"/>. If you do so, you'll quickly discover that -using the <command role="hg-cmd">hg diff</command> command to look at the history of changes to a -patch is unworkable. This is in part because you're looking at the -second derivative of the real code (a diff of a diff), but also -because MQ adds noise to the process by modifying time stamps and -directory names when it updates a patch. -</para> - -<para>However, you can use the <literal role="hg-ext">extdiff</literal> extension, which is bundled -with Mercurial, to turn a diff of two versions of a patch into -something readable. To do this, you will need a third-party package -called <literal role="package">patchutils</literal> <citation>web:patchutils</citation>. This provides a -command named <command>interdiff</command>, which shows the differences between -two diffs as a diff. Used on two versions of the same diff, it -generates a diff that represents the diff from the first to the second -version. -</para> - -<para>You can enable the <literal role="hg-ext">extdiff</literal> extension in the usual way, by -adding a line to the <literal role="rc-extensions">extensions</literal> section of your <filename role="special"> /.hgrc</filename>. -</para> -<programlisting> -<para> [extensions] - extdiff = -</para> -</programlisting> -<para>The <command>interdiff</command> command expects to be passed the names of two -files, but the <literal role="hg-ext">extdiff</literal> extension passes the program it runs a -pair of directories, each of which can contain an arbitrary number of -files. We thus need a small program that will run <command>interdiff</command> -on each pair of files in these two directories. This program is -available as <filename role="special">hg-interdiff</filename> in the <filename class="directory">examples</filename> -directory of the source code repository that accompanies this book. -<!-- &example.hg-interdiff; --> -</para> - -<para>With the <filename role="special">hg-interdiff</filename> program in your shell's search path, -you can run it as follows, from inside an MQ patch directory: -</para> -<programlisting> -<para> hg extdiff -p hg-interdiff -r A:B my-change.patch -</para> -</programlisting> -<para>Since you'll probably want to use this long-winded command a lot, you -can get <literal role="hg-ext">hgext</literal> to make it available as a normal Mercurial -command, again by editing your <filename role="special"> /.hgrc</filename>. -</para> -<programlisting> -<para> [extdiff] - cmd.interdiff = hg-interdiff -</para> -</programlisting> -<para>This directs <literal role="hg-ext">hgext</literal> to make an <literal>interdiff</literal> command -available, so you can now shorten the previous invocation of -<command role="hg-ext-extdiff">extdiff</command> to something a little more wieldy. -</para> -<programlisting> -<para> hg interdiff -r A:B my-change.patch -</para> -</programlisting> - -<note> -<para> The <command>interdiff</command> command works well only if the underlying - files against which versions of a patch are generated remain the - same. If you create a patch, modify the underlying files, and then - regenerate the patch, <command>interdiff</command> may not produce useful - output. -</para> -</note> - -<para>The <literal role="hg-ext">extdiff</literal> extension is useful for more than merely improving -the presentation of MQ patches. To read more about it, go to -section <xref linkend="sec:hgext:extdiff"/>. -</para> - -</sect2> -</sect1> +<chapter id="chap:mq-collab"> + <?dbhtml filename="advanced-uses-of-mercurial-queues.html"?> + <title>Advanced uses of Mercurial Queues</title> + + <para id="x_15d">While it's easy to pick up straightforward uses of Mercurial + Queues, use of a little discipline and some of MQ's less + frequently used capabilities makes it possible to work in + complicated development environments.</para> + + <para id="x_15e">In this chapter, I will use as an example a technique I have + used to manage the development of an Infiniband device driver for + the Linux kernel. The driver in question is large (at least as + drivers go), with 25,000 lines of code spread across 35 source + files. It is maintained by a small team of developers.</para> + + <para id="x_15f">While much of the material in this chapter is specific to + Linux, the same principles apply to any code base for which you're + not the primary owner, and upon which you need to do a lot of + development.</para> + + <sect1> + <title>The problem of many targets</title> + + <para id="x_160">The Linux kernel changes rapidly, and has never been + internally stable; developers frequently make drastic changes + between releases. This means that a version of the driver that + works well with a particular released version of the kernel will + not even <emphasis>compile</emphasis> correctly against, + typically, any other version.</para> + + <para id="x_161">To maintain a driver, we have to keep a number of distinct + versions of Linux in mind.</para> + <itemizedlist> + <listitem><para id="x_162">One target is the main Linux kernel development + tree. Maintenance of the code is in this case partly shared + by other developers in the kernel community, who make + <quote>drive-by</quote> modifications to the driver as they + develop and refine kernel subsystems.</para> + </listitem> + <listitem><para id="x_163">We also maintain a number of + <quote>backports</quote> to older versions of the Linux + kernel, to support the needs of customers who are running + older Linux distributions that do not incorporate our + drivers. (To <emphasis>backport</emphasis> a piece of code + is to modify it to work in an older version of its target + environment than the version it was developed for.)</para> + </listitem> + <listitem><para id="x_164">Finally, we make software releases on a schedule + that is necessarily not aligned with those used by Linux + distributors and kernel developers, so that we can deliver + new features to customers without forcing them to upgrade + their entire kernels or distributions.</para> + </listitem></itemizedlist> + + <sect2> + <title>Tempting approaches that don't work well</title> + + <para id="x_165">There are two <quote>standard</quote> ways to maintain a + piece of software that has to target many different + environments.</para> + + <para id="x_166">The first is to maintain a number of branches, each + intended for a single target. The trouble with this approach + is that you must maintain iron discipline in the flow of + changes between repositories. A new feature or bug fix must + start life in a <quote>pristine</quote> repository, then + percolate out to every backport repository. Backport changes + are more limited in the branches they should propagate to; a + backport change that is applied to a branch where it doesn't + belong will probably stop the driver from compiling.</para> + + <para id="x_167">The second is to maintain a single source tree filled with + conditional statements that turn chunks of code on or off + depending on the intended target. Because these + <quote>ifdefs</quote> are not allowed in the Linux kernel + tree, a manual or automatic process must be followed to strip + them out and yield a clean tree. A code base maintained in + this fashion rapidly becomes a rat's nest of conditional + blocks that are difficult to understand and maintain.</para> + + <para id="x_168">Neither of these approaches is well suited to a situation + where you don't <quote>own</quote> the canonical copy of a + source tree. In the case of a Linux driver that is + distributed with the standard kernel, Linus's tree contains + the copy of the code that will be treated by the world as + canonical. The upstream version of <quote>my</quote> driver + can be modified by people I don't know, without me even + finding out about it until after the changes show up in + Linus's tree.</para> + + <para id="x_169">These approaches have the added weakness of making it + difficult to generate well-formed patches to submit + upstream.</para> + + <para id="x_16a">In principle, Mercurial Queues seems like a good candidate + to manage a development scenario such as the above. While + this is indeed the case, MQ contains a few added features that + make the job more pleasant.</para> + + </sect2> + </sect1> + <sect1> + <title>Conditionally applying patches with guards</title> + + <para id="x_16b">Perhaps the best way to maintain sanity with so many targets + is to be able to choose specific patches to apply for a given + situation. MQ provides a feature called <quote>guards</quote> + (which originates with quilt's <literal>guards</literal> + command) that does just this. To start off, let's create a + simple repository for experimenting in.</para> + + &interaction.mq.guards.init; + + <para id="x_16c">This gives us a tiny repository that contains two patches + that don't have any dependencies on each other, because they + touch different files.</para> + + <para id="x_16d">The idea behind conditional application is that you can + <quote>tag</quote> a patch with a <emphasis>guard</emphasis>, + which is simply a text string of your choosing, then tell MQ to + select specific guards to use when applying patches. MQ will + then either apply, or skip over, a guarded patch, depending on + the guards that you have selected.</para> + + <para id="x_16e">A patch can have an arbitrary number of guards; each one is + <emphasis>positive</emphasis> (<quote>apply this patch if this + guard is selected</quote>) or <emphasis>negative</emphasis> + (<quote>skip this patch if this guard is selected</quote>). A + patch with no guards is always applied.</para> + + </sect1> + <sect1> + <title>Controlling the guards on a patch</title> + + <para id="x_16f">The <command role="hg-ext-mq">qguard</command> command lets + you determine which guards should apply to a patch, or display + the guards that are already in effect. Without any arguments, it + displays the guards on the current topmost patch.</para> + + &interaction.mq.guards.qguard; + + <para id="x_170">To set a positive guard on a patch, prefix the name of the + guard with a <quote><literal>+</literal></quote>.</para> + + &interaction.mq.guards.qguard.pos; + + <para id="x_171">To set a negative guard + on a patch, prefix the name of the guard with a + <quote><literal>-</literal></quote>.</para> + + &interaction.mq.guards.qguard.neg; + + <para id="x_74a">Notice that we prefixed the arguments to the <command>hg + qguard</command> command with a <literal>--</literal> here, so + that Mercurial would not interpret the text + <literal>-quux</literal> as an option.</para> + + <note> + <title>Setting vs. modifying</title> + + <para id="x_172"> The <command role="hg-ext-mq">qguard</command> command + <emphasis>sets</emphasis> the guards on a patch; it doesn't + <emphasis>modify</emphasis> them. What this means is that if + you run <command role="hg-cmd">hg qguard +a +b</command> on a + patch, then <command role="hg-cmd">hg qguard +c</command> on + the same patch, the <emphasis>only</emphasis> guard that will + be set on it afterwards is <literal>+c</literal>.</para> + </note> + + <para id="x_173">Mercurial stores guards in the <filename + role="special">series</filename> file; the form in which they + are stored is easy both to understand and to edit by hand. (In + other words, you don't have to use the <command + role="hg-ext-mq">qguard</command> command if you don't want + to; it's okay to simply edit the <filename + role="special">series</filename> file.)</para> + + &interaction.mq.guards.series; + + </sect1> + <sect1> + <title>Selecting the guards to use</title> + + <para id="x_174">The <command role="hg-ext-mq">qselect</command> command + determines which guards are active at a given time. The effect + of this is to determine which patches MQ will apply the next + time you run <command role="hg-ext-mq">qpush</command>. It has + no other effect; in particular, it doesn't do anything to + patches that are already applied.</para> + + <para id="x_175">With no arguments, the <command + role="hg-ext-mq">qselect</command> command lists the guards + currently in effect, one per line of output. Each argument is + treated as the name of a guard to apply.</para> + + &interaction.mq.guards.qselect.foo; + + <para id="x_176">In case you're interested, the currently selected guards are + stored in the <filename role="special">guards</filename> file.</para> + + &interaction.mq.guards.qselect.cat; + + <para id="x_177">We can see the effect the selected guards have when we run + <command role="hg-ext-mq">qpush</command>.</para> + + &interaction.mq.guards.qselect.qpush; + + <para id="x_178">A guard cannot start with a + <quote><literal>+</literal></quote> or + <quote><literal>-</literal></quote> character. The name of a + guard must not contain white space, but most other characters + are acceptable. If you try to use a guard with an invalid name, + MQ will complain:</para> + + &interaction.mq.guards.qselect.error; + + <para id="x_179">Changing the selected guards changes the patches that are + applied.</para> + + &interaction.mq.guards.qselect.quux; + + <para id="x_17a">You can see in the example below that negative guards take + precedence over positive guards.</para> + + &interaction.mq.guards.qselect.foobar; + + </sect1> + <sect1> + <title>MQ's rules for applying patches</title> + + <para id="x_17b">The rules that MQ uses when deciding whether to apply a + patch are as follows.</para> + <itemizedlist> + <listitem><para id="x_17c">A patch that has no guards is always + applied.</para> + </listitem> + <listitem><para id="x_17d">If the patch has any negative guard that matches + any currently selected guard, the patch is skipped.</para> + </listitem> + <listitem><para id="x_17e">If the patch has any positive guard that matches + any currently selected guard, the patch is applied.</para> + </listitem> + <listitem><para id="x_17f">If the patch has positive or negative guards, + but none matches any currently selected guard, the patch is + skipped.</para> + </listitem></itemizedlist> + + </sect1> + <sect1> + <title>Trimming the work environment</title> + + <para id="x_180">In working on the device driver I mentioned earlier, I don't + apply the patches to a normal Linux kernel tree. Instead, I use + a repository that contains only a snapshot of the source files + and headers that are relevant to Infiniband development. This + repository is 1% the size of a kernel repository, so it's easier + to work with.</para> + + <para id="x_181">I then choose a <quote>base</quote> version on top of which + the patches are applied. This is a snapshot of the Linux kernel + tree as of a revision of my choosing. When I take the snapshot, + I record the changeset ID from the kernel repository in the + commit message. Since the snapshot preserves the + <quote>shape</quote> and content of the relevant parts of the + kernel tree, I can apply my patches on top of either my tiny + repository or a normal kernel tree.</para> + + <para id="x_182">Normally, the base tree atop which the patches apply should + be a snapshot of a very recent upstream tree. This best + facilitates the development of patches that can easily be + submitted upstream with few or no modifications.</para> + + </sect1> + <sect1> + <title>Dividing up the <filename role="special">series</filename> + file</title> + + <para id="x_183">I categorise the patches in the <filename + role="special">series</filename> file into a number of logical + groups. Each section of like patches begins with a block of + comments that describes the purpose of the patches that + follow.</para> + + <para id="x_184">The sequence of patch groups that I maintain follows. The + ordering of these groups is important; I'll describe why after I + introduce the groups.</para> + <itemizedlist> + <listitem><para id="x_185">The <quote>accepted</quote> group. Patches that + the development team has submitted to the maintainer of the + Infiniband subsystem, and which he has accepted, but which + are not present in the snapshot that the tiny repository is + based on. These are <quote>read only</quote> patches, + present only to transform the tree into a similar state as + it is in the upstream maintainer's repository.</para> + </listitem> + <listitem><para id="x_186">The <quote>rework</quote> group. Patches that I + have submitted, but that the upstream maintainer has + requested modifications to before he will accept + them.</para> + </listitem> + <listitem><para id="x_187">The <quote>pending</quote> group. Patches that + I have not yet submitted to the upstream maintainer, but + which we have finished working on. These will be <quote>read + only</quote> for a while. If the upstream maintainer + accepts them upon submission, I'll move them to the end of + the <quote>accepted</quote> group. If he requests that I + modify any, I'll move them to the beginning of the + <quote>rework</quote> group.</para> + </listitem> + <listitem><para id="x_188">The <quote>in progress</quote> group. Patches + that are actively being developed, and should not be + submitted anywhere yet.</para> + </listitem> + <listitem><para id="x_189">The <quote>backport</quote> group. Patches that + adapt the source tree to older versions of the kernel + tree.</para> + </listitem> + <listitem><para id="x_18a">The <quote>do not ship</quote> group. Patches + that for some reason should never be submitted upstream. + For example, one such patch might change embedded driver + identification strings to make it easier to distinguish, in + the field, between an out-of-tree version of the driver and + a version shipped by a distribution vendor.</para> + </listitem></itemizedlist> + + <para id="x_18b">Now to return to the reasons for ordering groups of patches + in this way. We would like the lowest patches in the stack to + be as stable as possible, so that we will not need to rework + higher patches due to changes in context. Putting patches that + will never be changed first in the <filename + role="special">series</filename> file serves this + purpose.</para> + + <para id="x_18c">We would also like the patches that we know we'll need to + modify to be applied on top of a source tree that resembles the + upstream tree as closely as possible. This is why we keep + accepted patches around for a while.</para> + + <para id="x_18d">The <quote>backport</quote> and <quote>do not ship</quote> + patches float at the end of the <filename + role="special">series</filename> file. The backport patches + must be applied on top of all other patches, and the <quote>do + not ship</quote> patches might as well stay out of harm's + way.</para> + + </sect1> + <sect1> + <title>Maintaining the patch series</title> + + <para id="x_18e">In my work, I use a number of guards to control which + patches are to be applied.</para> + + <itemizedlist> + <listitem><para id="x_18f"><quote>Accepted</quote> patches are guarded with + <literal>accepted</literal>. I enable this guard most of + the time. When I'm applying the patches on top of a tree + where the patches are already present, I can turn this patch + off, and the patches that follow it will apply + cleanly.</para> + </listitem> + <listitem><para id="x_190">Patches that are <quote>finished</quote>, but + not yet submitted, have no guards. If I'm applying the + patch stack to a copy of the upstream tree, I don't need to + enable any guards in order to get a reasonably safe source + tree.</para> + </listitem> + <listitem><para id="x_191">Those patches that need reworking before being + resubmitted are guarded with + <literal>rework</literal>.</para> + </listitem> + <listitem><para id="x_192">For those patches that are still under + development, I use <literal>devel</literal>.</para> + </listitem> + <listitem><para id="x_193">A backport patch may have several guards, one + for each version of the kernel to which it applies. For + example, a patch that backports a piece of code to 2.6.9 + will have a <literal>2.6.9</literal> guard.</para> + </listitem></itemizedlist> + <para id="x_194">This variety of guards gives me considerable flexibility in + determining what kind of source tree I want to end up with. For + most situations, the selection of appropriate guards is + automated during the build process, but I can manually tune the + guards to use for less common circumstances.</para> + + <sect2> + <title>The art of writing backport patches</title> + + <para id="x_195">Using MQ, writing a backport patch is a simple process. + All such a patch has to do is modify a piece of code that uses + a kernel feature not present in the older version of the + kernel, so that the driver continues to work correctly under + that older version.</para> + + <para id="x_196">A useful goal when writing a good backport patch is to + make your code look as if it was written for the older version + of the kernel you're targeting. The less obtrusive the patch, + the easier it will be to understand and maintain. If you're + writing a collection of backport patches to avoid the + <quote>rat's nest</quote> effect of lots of + <literal>#ifdef</literal>s (hunks of source code that are only + used conditionally) in your code, don't introduce + version-dependent <literal>#ifdef</literal>s into the patches. + Instead, write several patches, each of which makes + unconditional changes, and control their application using + guards.</para> + + <para id="x_197">There are two reasons to divide backport patches into a + distinct group, away from the <quote>regular</quote> patches + whose effects they modify. The first is that intermingling the + two makes it more difficult to use a tool like the <literal + role="hg-ext">patchbomb</literal> extension to automate the + process of submitting the patches to an upstream maintainer. + The second is that a backport patch could perturb the context + in which a subsequent regular patch is applied, making it + impossible to apply the regular patch cleanly + <emphasis>without</emphasis> the earlier backport patch + already being applied.</para> + + </sect2> + </sect1> + <sect1> + <title>Useful tips for developing with MQ</title> + + <sect2> + <title>Organising patches in directories</title> + + <para id="x_198">If you're working on a substantial project with MQ, it's + not difficult to accumulate a large number of patches. For + example, I have one patch repository that contains over 250 + patches.</para> + + <para id="x_199">If you can group these patches into separate logical + categories, you can if you like store them in different + directories; MQ has no problems with patch names that contain + path separators.</para> + + </sect2> + <sect2 id="mq-collab:tips:interdiff"> + <title>Viewing the history of a patch</title> + + <para id="x_19a">If you're developing a set of patches over a long time, + it's a good idea to maintain them in a repository, as + discussed in <xref linkend="sec:mq:repo"/>. If you do + so, you'll quickly + discover that using the <command role="hg-cmd">hg + diff</command> command to look at the history of changes to + a patch is unworkable. This is in part because you're looking + at the second derivative of the real code (a diff of a diff), + but also because MQ adds noise to the process by modifying + time stamps and directory names when it updates a + patch.</para> + + <para id="x_19b">However, you can use the <literal + role="hg-ext">extdiff</literal> extension, which is bundled + with Mercurial, to turn a diff of two versions of a patch into + something readable. To do this, you will need a third-party + package called <literal role="package">patchutils</literal> + <citation>web:patchutils</citation>. This provides a command + named <command>interdiff</command>, which shows the + differences between two diffs as a diff. Used on two versions + of the same diff, it generates a diff that represents the diff + from the first to the second version.</para> + + <para id="x_19c">You can enable the <literal + role="hg-ext">extdiff</literal> extension in the usual way, + by adding a line to the <literal + role="rc-extensions">extensions</literal> section of your + <filename role="special">~/.hgrc</filename>.</para> + <programlisting>[extensions] +extdiff =</programlisting> + <para id="x_19d">The <command>interdiff</command> command expects to be + passed the names of two files, but the <literal + role="hg-ext">extdiff</literal> extension passes the program + it runs a pair of directories, each of which can contain an + arbitrary number of files. We thus need a small program that + will run <command>interdiff</command> on each pair of files in + these two directories. This program is available as <filename + role="special">hg-interdiff</filename> in the <filename + class="directory">examples</filename> directory of the + source code repository that accompanies this book. <!-- + &example.hg-interdiff; --></para> + + <para id="x_19e">With the <filename role="special">hg-interdiff</filename> + program in your shell's search path, you can run it as + follows, from inside an MQ patch directory:</para> + <programlisting>hg extdiff -p hg-interdiff -r A:B my-change.patch</programlisting> + <para id="x_19f">Since you'll probably want to use this long-winded command + a lot, you can get <literal role="hg-ext">hgext</literal> to + make it available as a normal Mercurial command, again by + editing your <filename + role="special">~/.hgrc</filename>.</para> + <programlisting>[extdiff] +cmd.interdiff = hg-interdiff</programlisting> + <para id="x_1a0">This directs <literal role="hg-ext">hgext</literal> to + make an <literal>interdiff</literal> command available, so you + can now shorten the previous invocation of <command + role="hg-ext-extdiff">extdiff</command> to something a + little more wieldy.</para> + <programlisting>hg interdiff -r A:B my-change.patch</programlisting> + + <note> + <para id="x_1a1"> The <command>interdiff</command> command works well + only if the underlying files against which versions of a + patch are generated remain the same. If you create a patch, + modify the underlying files, and then regenerate the patch, + <command>interdiff</command> may not produce useful + output.</para> + </note> + + <para id="x_1a2">The <literal role="hg-ext">extdiff</literal> extension is + useful for more than merely improving the presentation of MQ + patches. To read more about it, go to <xref + linkend="sec:hgext:extdiff"/>.</para> + + </sect2> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +--> diff -r c075fb0481c0 -r f0110009e946 fr/ch14-hgext.xml --- a/fr/ch14-hgext.xml Wed Sep 09 15:25:09 2009 +0200 +++ b/fr/ch14-hgext.xml Wed Sep 09 16:07:36 2009 +0200 @@ -1,539 +1,554 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> -<chapter> -<title>Adding functionality with extensions</title> -<para>\label{chap:hgext}</para> - -<para>While the core of Mercurial is quite complete from a functionality -standpoint, it's deliberately shorn of fancy features. This approach -of preserving simplicity keeps the software easy to deal with for both -maintainers and users.</para> - -<para>However, Mercurial doesn't box you in with an inflexible command set: -you can add features to it as <emphasis>extensions</emphasis> (sometimes known as -<emphasis>plugins</emphasis>). We've already discussed a few of these extensions in -earlier chapters.</para> -<itemizedlist> -<listitem><para>Section <xref linkend="sec:tour-merge:fetch"/> covers the <literal role="hg-ext">fetch</literal> - extension; this combines pulling new changes and merging them with - local changes into a single command, <command role="hg-ext-fetch">fetch</command>.</para> -</listitem> -<listitem><para>In chapter <xref linkend="chap:hook"/>, we covered several extensions that - are useful for hook-related functionality: <literal role="hg-ext">acl</literal> adds access - control lists; <literal role="hg-ext">bugzilla</literal> adds integration with the Bugzilla - bug tracking system; and <literal role="hg-ext">notify</literal> sends notification emails on - new changes.</para> -</listitem> -<listitem><para>The Mercurial Queues patch management extension is so invaluable - that it merits two chapters and an appendix all to itself. - Chapter <xref linkend="chap:mq"/> covers the basics; - chapter <xref linkend="chap:mq-collab"/> discusses advanced topics; and - appendix <xref linkend="chap:mqref"/> goes into detail on each command.</para> -</listitem></itemizedlist> - -<para>In this chapter, we'll cover some of the other extensions that are -available for Mercurial, and briefly touch on some of the machinery -you'll need to know about if you want to write an extension of your -own.</para> -<itemizedlist> -<listitem><para>In section <xref linkend="sec:hgext:inotify"/>, we'll discuss the - possibility of <emphasis>huge</emphasis> performance improvements using the - <literal role="hg-ext">inotify</literal> extension.</para> -</listitem></itemizedlist> - -<sect1> -<title>Improve performance with the <literal role="hg-ext">inotify</literal> extension</title> -<para>\label{sec:hgext:inotify} -</para> - -<para>Are you interested in having some of the most common Mercurial -operations run as much as a hundred times faster? Read on! -</para> - -<para>Mercurial has great performance under normal circumstances. For -example, when you run the <command role="hg-cmd">hg status</command> command, Mercurial has to -scan almost every directory and file in your repository so that it can -display file status. Many other Mercurial commands need to do the -same work behind the scenes; for example, the <command role="hg-cmd">hg diff</command> command -uses the status machinery to avoid doing an expensive comparison -operation on files that obviously haven't changed. -</para> - -<para>Because obtaining file status is crucial to good performance, the -authors of Mercurial have optimised this code to within an inch of its -life. However, there's no avoiding the fact that when you run -<command role="hg-cmd">hg status</command>, Mercurial is going to have to perform at least one -expensive system call for each managed file to determine whether it's -changed since the last time Mercurial checked. For a sufficiently -large repository, this can take a long time. -</para> - -<para>To put a number on the magnitude of this effect, I created a -repository containing 150,000 managed files. I timed <command role="hg-cmd">hg status</command> -as taking ten seconds to run, even when <emphasis>none</emphasis> of those files had -been modified. -</para> - -<para>Many modern operating systems contain a file notification facility. -If a program signs up to an appropriate service, the operating system -will notify it every time a file of interest is created, modified, or -deleted. On Linux systems, the kernel component that does this is -called <literal>inotify</literal>. -</para> - -<para>Mercurial's <literal role="hg-ext">inotify</literal> extension talks to the kernel's -<literal>inotify</literal> component to optimise <command role="hg-cmd">hg status</command> commands. The -extension has two components. A daemon sits in the background and -receives notifications from the <literal>inotify</literal> subsystem. It also -listens for connections from a regular Mercurial command. The -extension modifies Mercurial's behaviour so that instead of scanning -the filesystem, it queries the daemon. Since the daemon has perfect -information about the state of the repository, it can respond with a -result instantaneously, avoiding the need to scan every directory and -file in the repository. -</para> - -<para>Recall the ten seconds that I measured plain Mercurial as taking to -run <command role="hg-cmd">hg status</command> on a 150,000 file repository. With the -<literal role="hg-ext">inotify</literal> extension enabled, the time dropped to 0.1 seconds, a -factor of <emphasis>one hundred</emphasis> faster. -</para> - -<para>Before we continue, please pay attention to some caveats. -</para> -<itemizedlist> -<listitem><para>The <literal role="hg-ext">inotify</literal> extension is Linux-specific. Because it - interfaces directly to the Linux kernel's <literal>inotify</literal> - subsystem, it does not work on other operating systems. -</para> -</listitem> -<listitem><para>It should work on any Linux distribution that was released after - early 2005. Older distributions are likely to have a kernel that - lacks <literal>inotify</literal>, or a version of <literal>glibc</literal> that does not - have the necessary interfacing support. -</para> -</listitem> -<listitem><para>Not all filesystems are suitable for use with the - <literal role="hg-ext">inotify</literal> extension. Network filesystems such as NFS are a - non-starter, for example, particularly if you're running Mercurial - on several systems, all mounting the same network filesystem. The - kernel's <literal>inotify</literal> system has no way of knowing about changes - made on another system. Most local filesystems (e.g. ext3, XFS, - ReiserFS) should work fine. -</para> -</listitem></itemizedlist> - -<para>The <literal role="hg-ext">inotify</literal> extension is not yet shipped with Mercurial as of -May 2007, so it's a little more involved to set up than other -extensions. But the performance improvement is worth it! -</para> - -<para>The extension currently comes in two parts: a set of patches to the -Mercurial source code, and a library of Python bindings to the -<literal>inotify</literal> subsystem. -</para> -<note> -<para> There are <emphasis>two</emphasis> Python <literal>inotify</literal> binding libraries. One - of them is called <literal>pyinotify</literal>, and is packaged by some Linux - distributions as <literal>python-inotify</literal>. This is <emphasis>not</emphasis> the - one you'll need, as it is too buggy and inefficient to be practical. -</para> -</note> -<para>To get going, it's best to already have a functioning copy of -Mercurial installed. -</para> -<note> -<para> If you follow the instructions below, you'll be <emphasis>replacing</emphasis> and - overwriting any existing installation of Mercurial that you might - already have, using the latest <quote>bleeding edge</quote> Mercurial code. - Don't say you weren't warned! -</para> -</note> -<orderedlist> -<listitem><para>Clone the Python <literal>inotify</literal> binding repository. Build and - install it. -</para> -</listitem><programlisting> -<listitem><para> hg clone http://hg.kublai.com/python/inotify - cd inotify - python setup.py build --force - sudo python setup.py install --skip-build -</para> -</listitem></programlisting> -</para> -</listitem> -<listitem><para>Clone the <filename class="directory">crew</filename> Mercurial repository. Clone the - <literal role="hg-ext">inotify</literal> patch repository so that Mercurial Queues will be - able to apply patches to your cope of the <filename class="directory">crew</filename> repository. -</para> -</listitem><programlisting> -<listitem><para> hg clone http://hg.intevation.org/mercurial/crew - hg clone crew inotify - hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches -</para> -</listitem></programlisting> -</para> -</listitem> -<listitem><para>Make sure that you have the Mercurial Queues extension, - <literal role="hg-ext">mq</literal>, enabled. If you've never used MQ, read - section <xref linkend="sec:mq:start"/> to get started quickly. -</para> -</listitem> -<listitem><para>Go into the <filename class="directory">inotify</filename> repo, and apply all of the - <literal role="hg-ext">inotify</literal> patches using the <option role="hg-ext-mq-cmd-qpush-opt">-a</option> option to - the <command role="hg-ext-mq">qpush</command> command. -</para> -</listitem><programlisting> -<listitem><para> cd inotify - hg qpush -a -</para> -</listitem></programlisting> -<listitem><para> If you get an error message from <command role="hg-ext-mq">qpush</command>, you should not - continue. Instead, ask for help. -</para> -</listitem> -<listitem><para>Build and install the patched version of Mercurial. -</para> -</listitem><programlisting> -<listitem><para> python setup.py build --force - sudo python setup.py install --skip-build -</para> -</listitem></programlisting> -</orderedlist> -<para>Once you've build a suitably patched version of Mercurial, all you -need to do to enable the <literal role="hg-ext">inotify</literal> extension is add an entry to -your <filename role="special"> /.hgrc</filename>. -</para> -<programlisting> -<para> [extensions] - inotify = -</para> -</programlisting> -<para>When the <literal role="hg-ext">inotify</literal> extension is enabled, Mercurial will -automatically and transparently start the status daemon the first time -you run a command that needs status in a repository. It runs one -status daemon per repository. -</para> - -<para>The status daemon is started silently, and runs in the background. If -you look at a list of running processes after you've enabled the -<literal role="hg-ext">inotify</literal> extension and run a few commands in different -repositories, you'll thus see a few <literal>hg</literal> processes sitting -around, waiting for updates from the kernel and queries from -Mercurial. -</para> - -<para>The first time you run a Mercurial command in a repository when you -have the <literal role="hg-ext">inotify</literal> extension enabled, it will run with about the -same performance as a normal Mercurial command. This is because the -status daemon needs to perform a normal status scan so that it has a -baseline against which to apply later updates from the kernel. -However, <emphasis>every</emphasis> subsequent command that does any kind of status -check should be noticeably faster on repositories of even fairly -modest size. Better yet, the bigger your repository is, the greater a -performance advantage you'll see. The <literal role="hg-ext">inotify</literal> daemon makes -status operations almost instantaneous on repositories of all sizes! -</para> - -<para>If you like, you can manually start a status daemon using the -<command role="hg-ext-inotify">inserve</command> command. This gives you slightly finer -control over how the daemon ought to run. This command will of course -only be available when the <literal role="hg-ext">inotify</literal> extension is enabled. -</para> - -<para>When you're using the <literal role="hg-ext">inotify</literal> extension, you should notice -<emphasis>no difference at all</emphasis> in Mercurial's behaviour, with the sole -exception of status-related commands running a whole lot faster than -they used to. You should specifically expect that commands will not -print different output; neither should they give different results. -If either of these situations occurs, please report a bug. -</para> - -</sect1> -<sect1> -<title>Flexible diff support with the <literal role="hg-ext">extdiff</literal> extension</title> -<para>\label{sec:hgext:extdiff} -</para> - -<para>Mercurial's built-in <command role="hg-cmd">hg diff</command> command outputs plaintext unified -diffs. -<!-- &interaction.extdiff.diff; --> -If you would like to use an external tool to display modifications, -you'll want to use the <literal role="hg-ext">extdiff</literal> extension. This will let you -use, for example, a graphical diff tool. -</para> - -<para>The <literal role="hg-ext">extdiff</literal> extension is bundled with Mercurial, so it's easy -to set up. In the <literal role="rc-extensions">extensions</literal> section of your <filename role="special"> /.hgrc</filename>, -simply add a one-line entry to enable the extension. -</para> -<programlisting> -<para> [extensions] - extdiff = -</para> -</programlisting> -<para>This introduces a command named <command role="hg-ext-extdiff">extdiff</command>, which by -default uses your system's <command>diff</command> command to generate a -unified diff in the same form as the built-in <command role="hg-cmd">hg diff</command> command. -<!-- &interaction.extdiff.extdiff; --> -The result won't be exactly the same as with the built-in <command role="hg-cmd">hg diff</command> -variations, because the output of <command>diff</command> varies from one -system to another, even when passed the same options. -</para> - -<para>As the <quote><literal>making snapshot</literal></quote> lines of output above imply, the -<command role="hg-ext-extdiff">extdiff</command> command works by creating two snapshots of -your source tree. The first snapshot is of the source revision; the -second, of the target revision or working directory. The -<command role="hg-ext-extdiff">extdiff</command> command generates these snapshots in a -temporary directory, passes the name of each directory to an external -diff viewer, then deletes the temporary directory. For efficiency, it -only snapshots the directories and files that have changed between the -two revisions. -</para> - -<para>Snapshot directory names have the same base name as your repository. -If your repository path is <filename class="directory">/quux/bar/foo</filename>, then <filename class="directory">foo</filename> -will be the name of each snapshot directory. Each snapshot directory -name has its changeset ID appended, if appropriate. If a snapshot is -of revision <literal>a631aca1083f</literal>, the directory will be named -<filename class="directory">foo.a631aca1083f</filename>. A snapshot of the working directory won't -have a changeset ID appended, so it would just be <filename class="directory">foo</filename> in -this example. To see what this looks like in practice, look again at -the <command role="hg-ext-extdiff">extdiff</command> example above. Notice that the diff has -the snapshot directory names embedded in its header. -</para> - -<para>The <command role="hg-ext-extdiff">extdiff</command> command accepts two important options. -The <option role="hg-ext-extdiff-cmd-extdiff-opt">-p</option> option lets you choose a program to -view differences with, instead of <command>diff</command>. With the -<option role="hg-ext-extdiff-cmd-extdiff-opt">-o</option> option, you can change the options that -<command role="hg-ext-extdiff">extdiff</command> passes to the program (by default, these -options are <quote><literal>-Npru</literal></quote>, which only make sense if you're -running <command>diff</command>). In other respects, the -<command role="hg-ext-extdiff">extdiff</command> command acts similarly to the built-in -<command role="hg-cmd">hg diff</command> command: you use the same option names, syntax, and -arguments to specify the revisions you want, the files you want, and -so on. -</para> - -<para>As an example, here's how to run the normal system <command>diff</command> -command, getting it to generate context diffs (using the -<option role="cmd-opt-diff">-c</option> option) instead of unified diffs, and five lines of -context instead of the default three (passing <literal>5</literal> as the -argument to the <option role="cmd-opt-diff">-C</option> option). -<!-- &interaction.extdiff.extdiff-ctx; --> -</para> - -<para>Launching a visual diff tool is just as easy. Here's how to launch -the <command>kdiff3</command> viewer. -</para> -<programlisting> -<para> hg extdiff -p kdiff3 -o </quote> -</para> -</programlisting> - -<para>If your diff viewing command can't deal with directories, you can -easily work around this with a little scripting. For an example of -such scripting in action with the <literal role="hg-ext">mq</literal> extension and the -<command>interdiff</command> command, see -section <xref linkend="mq-collab:tips:interdiff"/>. -</para> - -<sect2> -<title>Defining command aliases</title> - -<para>It can be cumbersome to remember the options to both the -<command role="hg-ext-extdiff">extdiff</command> command and the diff viewer you want to use, -so the <literal role="hg-ext">extdiff</literal> extension lets you define <emphasis>new</emphasis> commands -that will invoke your diff viewer with exactly the right options. -</para> - -<para>All you need to do is edit your <filename role="special"> /.hgrc</filename>, and add a section named -<literal role="rc-extdiff">extdiff</literal>. Inside this section, you can define multiple -commands. Here's how to add a <literal>kdiff3</literal> command. Once you've -defined this, you can type <quote><literal>hg kdiff3</literal></quote> and the -<literal role="hg-ext">extdiff</literal> extension will run <command>kdiff3</command> for you. -</para> -<programlisting> -<para> [extdiff] - cmd.kdiff3 = -</para> -</programlisting> -<para>If you leave the right hand side of the definition empty, as above, -the <literal role="hg-ext">extdiff</literal> extension uses the name of the command you defined -as the name of the external program to run. But these names don't -have to be the same. Here, we define a command named <quote>\texttt{hg - wibble}</quote>, which runs <command>kdiff3</command>. -</para> -<programlisting> -<para> [extdiff] - cmd.wibble = kdiff3 -</para> -</programlisting> - -<para>You can also specify the default options that you want to invoke your -diff viewing program with. The prefix to use is <quote><literal>opts.</literal></quote>, -followed by the name of the command to which the options apply. This -example defines a <quote><literal>hg vimdiff</literal></quote> command that runs the -<command>vim</command> editor's <literal>DirDiff</literal> extension. -</para> -<programlisting> -<para> [extdiff] - cmd.vimdiff = vim - opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' -</para> -</programlisting> - -</sect2> -</sect1> -<sect1> -<title>Cherrypicking changes with the <literal role="hg-ext">transplant</literal> extension</title> -<para>\label{sec:hgext:transplant} -</para> - -<para>Need to have a long chat with Brendan about this. -</para> - -</sect1> -<sect1> -<title>Send changes via email with the <literal role="hg-ext">patchbomb</literal> extension</title> -<para>\label{sec:hgext:patchbomb} -</para> - -<para>Many projects have a culture of <quote>change review</quote>, in which people -send their modifications to a mailing list for others to read and -comment on before they commit the final version to a shared -repository. Some projects have people who act as gatekeepers; they -apply changes from other people to a repository to which those others -don't have access. -</para> - -<para>Mercurial makes it easy to send changes over email for review or -application, via its <literal role="hg-ext">patchbomb</literal> extension. The extension is so -namd because changes are formatted as patches, and it's usual to send -one changeset per email message. Sending a long series of changes by -email is thus much like <quote>bombing</quote> the recipient's inbox, hence -<quote>patchbomb</quote>. -</para> - -<para>As usual, the basic configuration of the <literal role="hg-ext">patchbomb</literal> extension -takes just one or two lines in your <filename role="special"> /.hgrc</filename>. -</para> -<programlisting> -<para> [extensions] - patchbomb = -</para> -</programlisting> -<para>Once you've enabled the extension, you will have a new command -available, named <command role="hg-ext-patchbomb">email</command>. -</para> - -<para>The safest and best way to invoke the <command role="hg-ext-patchbomb">email</command> -command is to <emphasis>always</emphasis> run it first with the -<option role="hg-ext-patchbomb-cmd-email-opt">-n</option> option. This will show you what the -command <emphasis>would</emphasis> send, without actually sending anything. Once -you've had a quick glance over the changes and verified that you are -sending the right ones, you can rerun the same command, with the -<option role="hg-ext-patchbomb-cmd-email-opt">-n</option> option removed. -</para> - -<para>The <command role="hg-ext-patchbomb">email</command> command accepts the same kind of -revision syntax as every other Mercurial command. For example, this -command will send every revision between 7 and <literal>tip</literal>, -inclusive. -</para> -<programlisting> -<para> hg email -n 7:tip -</para> -</programlisting> -<para>You can also specify a <emphasis>repository</emphasis> to compare with. If you -provide a repository but no revisions, the <command role="hg-ext-patchbomb">email</command> -command will send all revisions in the local repository that are not -present in the remote repository. If you additionally specify -revisions or a branch name (the latter using the -<option role="hg-ext-patchbomb-cmd-email-opt">-b</option> option), this will constrain the -revisions sent. -</para> - -<para>It's perfectly safe to run the <command role="hg-ext-patchbomb">email</command> command -without the names of the people you want to send to: if you do this, -it will just prompt you for those values interactively. (If you're -using a Linux or Unix-like system, you should have enhanced -<literal>readline</literal>-style editing capabilities when entering those -headers, too, which is useful.) -</para> - -<para>When you are sending just one revision, the <command role="hg-ext-patchbomb">email</command> -command will by default use the first line of the changeset -description as the subject of the single email message it sends. -</para> - -<para>If you send multiple revisions, the <command role="hg-ext-patchbomb">email</command> command -will usually send one message per changeset. It will preface the -series with an introductory message, in which you should describe the -purpose of the series of changes you're sending. -</para> - -<sect2> -<title>Changing the behaviour of patchbombs</title> - -<para>Not every project has exactly the same conventions for sending changes -in email; the <literal role="hg-ext">patchbomb</literal> extension tries to accommodate a -number of variations through command line options. -</para> -<itemizedlist> -<listitem><para>You can write a subject for the introductory message on the - command line using the <option role="hg-ext-patchbomb-cmd-email-opt">-s</option> option. This - takes one argument, the text of the subject to use. -</para> -</listitem> -<listitem><para>To change the email address from which the messages originate, - use the <option role="hg-ext-patchbomb-cmd-email-opt">-f</option> option. This takes one - argument, the email address to use. -</para> -</listitem> -<listitem><para>The default behaviour is to send unified diffs (see - section <xref linkend="sec:mq:patch"/> for a description of the format), one per - message. You can send a binary bundle instead with the - <option role="hg-ext-patchbomb-cmd-email-opt">-b</option> option. -</para> -</listitem> -<listitem><para>Unified diffs are normally prefaced with a metadata header. You - can omit this, and send unadorned diffs, with the - <option role="hg-ext-patchbomb-cmd-email-opt">--plain</option> option. -</para> -</listitem> -<listitem><para>Diffs are normally sent <quote>inline</quote>, in the same body part as the - description of a patch. This makes it easiest for the largest - number of readers to quote and respond to parts of a diff, as some - mail clients will only quote the first MIME body part in a message. - If you'd prefer to send the description and the diff in separate - body parts, use the <option role="hg-ext-patchbomb-cmd-email-opt">-a</option> option. -</para> -</listitem> -<listitem><para>Instead of sending mail messages, you can write them to an - <literal>mbox</literal>-format mail folder using the - <option role="hg-ext-patchbomb-cmd-email-opt">-m</option> option. That option takes one - argument, the name of the file to write to. -</para> -</listitem> -<listitem><para>If you would like to add a <command>diffstat</command>-format summary to - each patch, and one to the introductory message, use the - <option role="hg-ext-patchbomb-cmd-email-opt">-d</option> option. The <command>diffstat</command> - command displays a table containing the name of each file patched, - the number of lines affected, and a histogram showing how much each - file is modified. This gives readers a qualitative glance at how - complex a patch is. -</para> -</listitem></itemizedlist> - -</sect2> -</sect1> +<chapter id="chap:hgext"> + <?dbhtml filename="adding-functionality-with-extensions.html"?> + <title>Adding functionality with extensions</title> + + <para id="x_4fe">While the core of Mercurial is quite complete from a + functionality standpoint, it's deliberately shorn of fancy + features. This approach of preserving simplicity keeps the + software easy to deal with for both maintainers and users.</para> + + <para id="x_4ff">However, Mercurial doesn't box you in with an inflexible + command set: you can add features to it as + <emphasis>extensions</emphasis> (sometimes known as + <emphasis>plugins</emphasis>). We've already discussed a few of + these extensions in earlier chapters.</para> + <itemizedlist> + <listitem><para id="x_500"><xref linkend="sec:tour-merge:fetch"/> + covers the <literal role="hg-ext">fetch</literal> extension; + this combines pulling new changes and merging them with local + changes into a single command, <command + role="hg-ext-fetch">fetch</command>.</para> + </listitem> + <listitem><para id="x_501">In <xref linkend="chap:hook"/>, we covered + several extensions that are useful for hook-related + functionality: <literal role="hg-ext">acl</literal> adds + access control lists; <literal + role="hg-ext">bugzilla</literal> adds integration with the + Bugzilla bug tracking system; and <literal + role="hg-ext">notify</literal> sends notification emails on + new changes.</para> + </listitem> + <listitem><para id="x_502">The Mercurial Queues patch management extension is + so invaluable that it merits two chapters and an appendix all + to itself. <xref linkend="chap:mq"/> covers the + basics; <xref + linkend="chap:mq-collab"/> discusses advanced topics; + and <xref linkend="chap:mqref"/> goes into detail on + each + command.</para> + </listitem></itemizedlist> + + <para id="x_503">In this chapter, we'll cover some of the other extensions that + are available for Mercurial, and briefly touch on some of the + machinery you'll need to know about if you want to write an + extension of your own.</para> + <itemizedlist> + <listitem><para id="x_504">In <xref linkend="sec:hgext:inotify"/>, + we'll discuss the possibility of <emphasis>huge</emphasis> + performance improvements using the <literal + role="hg-ext">inotify</literal> extension.</para> + </listitem></itemizedlist> + + <sect1 id="sec:hgext:inotify"> + <title>Improve performance with the <literal + role="hg-ext">inotify</literal> extension</title> + + <para id="x_505">Are you interested in having some of the most common + Mercurial operations run as much as a hundred times faster? + Read on!</para> + + <para id="x_506">Mercurial has great performance under normal circumstances. + For example, when you run the <command role="hg-cmd">hg + status</command> command, Mercurial has to scan almost every + directory and file in your repository so that it can display + file status. Many other Mercurial commands need to do the same + work behind the scenes; for example, the <command + role="hg-cmd">hg diff</command> command uses the status + machinery to avoid doing an expensive comparison operation on + files that obviously haven't changed.</para> + + <para id="x_507">Because obtaining file status is crucial to good + performance, the authors of Mercurial have optimised this code + to within an inch of its life. However, there's no avoiding the + fact that when you run <command role="hg-cmd">hg + status</command>, Mercurial is going to have to perform at + least one expensive system call for each managed file to + determine whether it's changed since the last time Mercurial + checked. For a sufficiently large repository, this can take a + long time.</para> + + <para id="x_508">To put a number on the magnitude of this effect, I created a + repository containing 150,000 managed files. I timed <command + role="hg-cmd">hg status</command> as taking ten seconds to + run, even when <emphasis>none</emphasis> of those files had been + modified.</para> + + <para id="x_509">Many modern operating systems contain a file notification + facility. If a program signs up to an appropriate service, the + operating system will notify it every time a file of interest is + created, modified, or deleted. On Linux systems, the kernel + component that does this is called + <literal>inotify</literal>.</para> + + <para id="x_50a">Mercurial's <literal role="hg-ext">inotify</literal> + extension talks to the kernel's <literal>inotify</literal> + component to optimise <command role="hg-cmd">hg status</command> + commands. The extension has two components. A daemon sits in + the background and receives notifications from the + <literal>inotify</literal> subsystem. It also listens for + connections from a regular Mercurial command. The extension + modifies Mercurial's behavior so that instead of scanning the + filesystem, it queries the daemon. Since the daemon has perfect + information about the state of the repository, it can respond + with a result instantaneously, avoiding the need to scan every + directory and file in the repository.</para> + + <para id="x_50b">Recall the ten seconds that I measured plain Mercurial as + taking to run <command role="hg-cmd">hg status</command> on a + 150,000 file repository. With the <literal + role="hg-ext">inotify</literal> extension enabled, the time + dropped to 0.1 seconds, a factor of <emphasis>one + hundred</emphasis> faster.</para> + + <para id="x_50c">Before we continue, please pay attention to some + caveats.</para> + <itemizedlist> + <listitem><para id="x_50d">The <literal role="hg-ext">inotify</literal> + extension is Linux-specific. Because it interfaces directly + to the Linux kernel's <literal>inotify</literal> subsystem, + it does not work on other operating systems.</para> + </listitem> + <listitem><para id="x_50e">It should work on any Linux distribution that + was released after early 2005. Older distributions are + likely to have a kernel that lacks + <literal>inotify</literal>, or a version of + <literal>glibc</literal> that does not have the necessary + interfacing support.</para> + </listitem> + <listitem><para id="x_50f">Not all filesystems are suitable for use with + the <literal role="hg-ext">inotify</literal> extension. + Network filesystems such as NFS are a non-starter, for + example, particularly if you're running Mercurial on several + systems, all mounting the same network filesystem. The + kernel's <literal>inotify</literal> system has no way of + knowing about changes made on another system. Most local + filesystems (e.g. ext3, XFS, ReiserFS) should work + fine.</para> + </listitem></itemizedlist> + + <para id="x_510">The <literal role="hg-ext">inotify</literal> extension is + not yet shipped with Mercurial as of May 2007, so it's a little + more involved to set up than other extensions. But the + performance improvement is worth it!</para> + + <para id="x_511">The extension currently comes in two parts: a set of patches + to the Mercurial source code, and a library of Python bindings + to the <literal>inotify</literal> subsystem.</para> + <note> + <para id="x_512"> There are <emphasis>two</emphasis> Python + <literal>inotify</literal> binding libraries. One of them is + called <literal>pyinotify</literal>, and is packaged by some + Linux distributions as <literal>python-inotify</literal>. + This is <emphasis>not</emphasis> the one you'll need, as it is + too buggy and inefficient to be practical.</para> + </note> + <para id="x_513">To get going, it's best to already have a functioning copy + of Mercurial installed.</para> + <note> + <para id="x_514"> If you follow the instructions below, you'll be + <emphasis>replacing</emphasis> and overwriting any existing + installation of Mercurial that you might already have, using + the latest <quote>bleeding edge</quote> Mercurial code. Don't + say you weren't warned!</para> + </note> + <orderedlist> + <listitem><para id="x_515">Clone the Python <literal>inotify</literal> + binding repository. Build and install it.</para> + <programlisting>hg clone http://hg.kublai.com/python/inotify +cd inotify +python setup.py build --force +sudo python setup.py install --skip-build</programlisting> + </listitem> + <listitem><para id="x_516">Clone the <filename + class="directory">crew</filename> Mercurial repository. + Clone the <literal role="hg-ext">inotify</literal> patch + repository so that Mercurial Queues will be able to apply + patches to your cope of the <filename + class="directory">crew</filename> repository.</para> + <programlisting>hg clone http://hg.intevation.org/mercurial/crew +hg clone crew inotify +hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches</programlisting> + </listitem> + <listitem><para id="x_517">Make sure that you have the Mercurial Queues + extension, <literal role="hg-ext">mq</literal>, enabled. If + you've never used MQ, read <xref + linkend="sec:mq:start"/> to get started + quickly.</para> + </listitem> + <listitem><para id="x_518">Go into the <filename + class="directory">inotify</filename> repo, and apply all + of the <literal role="hg-ext">inotify</literal> patches + using the <option role="hg-ext-mq-cmd-qpush-opt">hg + -a</option> option to the <command + role="hg-ext-mq">qpush</command> command.</para> + <programlisting>cd inotify +hg qpush -a</programlisting> + </listitem> + <listitem><para id="x_519"> If you get an error message from <command + role="hg-ext-mq">qpush</command>, you should not continue. + Instead, ask for help.</para> + </listitem> + <listitem><para id="x_51a">Build and install the patched version of + Mercurial.</para> + <programlisting>python setup.py build --force +sudo python setup.py install --skip-build</programlisting> + </listitem> + </orderedlist> + <para id="x_51b">Once you've build a suitably patched version of Mercurial, + all you need to do to enable the <literal + role="hg-ext">inotify</literal> extension is add an entry to + your <filename role="special">~/.hgrc</filename>.</para> + <programlisting>[extensions] inotify =</programlisting> + <para id="x_51c">When the <literal role="hg-ext">inotify</literal> extension + is enabled, Mercurial will automatically and transparently start + the status daemon the first time you run a command that needs + status in a repository. It runs one status daemon per + repository.</para> + + <para id="x_51d">The status daemon is started silently, and runs in the + background. If you look at a list of running processes after + you've enabled the <literal role="hg-ext">inotify</literal> + extension and run a few commands in different repositories, + you'll thus see a few <literal>hg</literal> processes sitting + around, waiting for updates from the kernel and queries from + Mercurial.</para> + + <para id="x_51e">The first time you run a Mercurial command in a repository + when you have the <literal role="hg-ext">inotify</literal> + extension enabled, it will run with about the same performance + as a normal Mercurial command. This is because the status + daemon needs to perform a normal status scan so that it has a + baseline against which to apply later updates from the kernel. + However, <emphasis>every</emphasis> subsequent command that does + any kind of status check should be noticeably faster on + repositories of even fairly modest size. Better yet, the bigger + your repository is, the greater a performance advantage you'll + see. The <literal role="hg-ext">inotify</literal> daemon makes + status operations almost instantaneous on repositories of all + sizes!</para> + + <para id="x_51f">If you like, you can manually start a status daemon using + the <command role="hg-ext-inotify">inserve</command> command. + This gives you slightly finer control over how the daemon ought + to run. This command will of course only be available when the + <literal role="hg-ext">inotify</literal> extension is + enabled.</para> + + <para id="x_520">When you're using the <literal + role="hg-ext">inotify</literal> extension, you should notice + <emphasis>no difference at all</emphasis> in Mercurial's + behavior, with the sole exception of status-related commands + running a whole lot faster than they used to. You should + specifically expect that commands will not print different + output; neither should they give different results. If either of + these situations occurs, please report a bug.</para> + + </sect1> + <sect1 id="sec:hgext:extdiff"> + <title>Flexible diff support with the <literal + role="hg-ext">extdiff</literal> extension</title> + + <para id="x_521">Mercurial's built-in <command role="hg-cmd">hg + diff</command> command outputs plaintext unified diffs.</para> + + &interaction.extdiff.diff; + + <para id="x_522">If you would like to use an external tool to display + modifications, you'll want to use the <literal + role="hg-ext">extdiff</literal> extension. This will let you + use, for example, a graphical diff tool.</para> + + <para id="x_523">The <literal role="hg-ext">extdiff</literal> extension is + bundled with Mercurial, so it's easy to set up. In the <literal + role="rc-extensions">extensions</literal> section of your + <filename role="special">~/.hgrc</filename>, simply add a + one-line entry to enable the extension.</para> + <programlisting>[extensions] +extdiff =</programlisting> + <para id="x_524">This introduces a command named <command + role="hg-ext-extdiff">extdiff</command>, which by default uses + your system's <command>diff</command> command to generate a + unified diff in the same form as the built-in <command + role="hg-cmd">hg diff</command> command.</para> + + &interaction.extdiff.extdiff; + + <para id="x_525">The result won't be exactly the same as with the built-in + <command role="hg-cmd">hg diff</command> variations, because the + output of <command>diff</command> varies from one system to + another, even when passed the same options.</para> + + <para id="x_526">As the <quote><literal>making snapshot</literal></quote> + lines of output above imply, the <command + role="hg-ext-extdiff">extdiff</command> command works by + creating two snapshots of your source tree. The first snapshot + is of the source revision; the second, of the target revision or + working directory. The <command + role="hg-ext-extdiff">extdiff</command> command generates + these snapshots in a temporary directory, passes the name of + each directory to an external diff viewer, then deletes the + temporary directory. For efficiency, it only snapshots the + directories and files that have changed between the two + revisions.</para> + + <para id="x_527">Snapshot directory names have the same base name as your + repository. If your repository path is <filename + class="directory">/quux/bar/foo</filename>, then <filename + class="directory">foo</filename> will be the name of each + snapshot directory. Each snapshot directory name has its + changeset ID appended, if appropriate. If a snapshot is of + revision <literal>a631aca1083f</literal>, the directory will be + named <filename class="directory">foo.a631aca1083f</filename>. + A snapshot of the working directory won't have a changeset ID + appended, so it would just be <filename + class="directory">foo</filename> in this example. To see what + this looks like in practice, look again at the <command + role="hg-ext-extdiff">extdiff</command> example above. Notice + that the diff has the snapshot directory names embedded in its + header.</para> + + <para id="x_528">The <command role="hg-ext-extdiff">extdiff</command> command + accepts two important options. The <option + role="hg-ext-extdiff-cmd-extdiff-opt">hg -p</option> option + lets you choose a program to view differences with, instead of + <command>diff</command>. With the <option + role="hg-ext-extdiff-cmd-extdiff-opt">hg -o</option> option, + you can change the options that <command + role="hg-ext-extdiff">extdiff</command> passes to the program + (by default, these options are + <quote><literal>-Npru</literal></quote>, which only make sense + if you're running <command>diff</command>). In other respects, + the <command role="hg-ext-extdiff">extdiff</command> command + acts similarly to the built-in <command role="hg-cmd">hg + diff</command> command: you use the same option names, syntax, + and arguments to specify the revisions you want, the files you + want, and so on.</para> + + <para id="x_529">As an example, here's how to run the normal system + <command>diff</command> command, getting it to generate context + diffs (using the <option role="cmd-opt-diff">-c</option> option) + instead of unified diffs, and five lines of context instead of + the default three (passing <literal>5</literal> as the argument + to the <option role="cmd-opt-diff">-C</option> option).</para> + + &interaction.extdiff.extdiff-ctx; + + <para id="x_52a">Launching a visual diff tool is just as easy. Here's how to + launch the <command>kdiff3</command> viewer.</para> + <programlisting>hg extdiff -p kdiff3 -o</programlisting> + + <para id="x_52b">If your diff viewing command can't deal with directories, + you can easily work around this with a little scripting. For an + example of such scripting in action with the <literal + role="hg-ext">mq</literal> extension and the + <command>interdiff</command> command, see <xref + linkend="mq-collab:tips:interdiff"/>.</para> + + <sect2> + <title>Defining command aliases</title> + + <para id="x_52c">It can be cumbersome to remember the options to both the + <command role="hg-ext-extdiff">extdiff</command> command and + the diff viewer you want to use, so the <literal + role="hg-ext">extdiff</literal> extension lets you define + <emphasis>new</emphasis> commands that will invoke your diff + viewer with exactly the right options.</para> + + <para id="x_52d">All you need to do is edit your <filename + role="special">~/.hgrc</filename>, and add a section named + <literal role="rc-extdiff">extdiff</literal>. Inside this + section, you can define multiple commands. Here's how to add + a <literal>kdiff3</literal> command. Once you've defined + this, you can type <quote><literal>hg kdiff3</literal></quote> + and the <literal role="hg-ext">extdiff</literal> extension + will run <command>kdiff3</command> for you.</para> + <programlisting>[extdiff] +cmd.kdiff3 =</programlisting> + <para id="x_52e">If you leave the right hand side of the definition empty, + as above, the <literal role="hg-ext">extdiff</literal> + extension uses the name of the command you defined as the name + of the external program to run. But these names don't have to + be the same. Here, we define a command named + <quote><literal>hg wibble</literal></quote>, which runs + <command>kdiff3</command>.</para> + <programlisting>[extdiff] + cmd.wibble = kdiff3</programlisting> + + <para id="x_52f">You can also specify the default options that you want to + invoke your diff viewing program with. The prefix to use is + <quote><literal>opts.</literal></quote>, followed by the name + of the command to which the options apply. This example + defines a <quote><literal>hg vimdiff</literal></quote> command + that runs the <command>vim</command> editor's + <literal>DirDiff</literal> extension.</para> + <programlisting>[extdiff] + cmd.vimdiff = vim +opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)'</programlisting> + + </sect2> + </sect1> + <sect1 id="sec:hgext:transplant"> + <title>Cherrypicking changes with the <literal + role="hg-ext">transplant</literal> extension</title> + + <para id="x_530">Need to have a long chat with Brendan about this.</para> + + </sect1> + <sect1 id="sec:hgext:patchbomb"> + <title>Send changes via email with the <literal + role="hg-ext">patchbomb</literal> extension</title> + + <para id="x_531">Many projects have a culture of <quote>change + review</quote>, in which people send their modifications to a + mailing list for others to read and comment on before they + commit the final version to a shared repository. Some projects + have people who act as gatekeepers; they apply changes from + other people to a repository to which those others don't have + access.</para> + + <para id="x_532">Mercurial makes it easy to send changes over email for + review or application, via its <literal + role="hg-ext">patchbomb</literal> extension. The extension is + so named because changes are formatted as patches, and it's usual + to send one changeset per email message. Sending a long series + of changes by email is thus much like <quote>bombing</quote> the + recipient's inbox, hence <quote>patchbomb</quote>.</para> + + <para id="x_533">As usual, the basic configuration of the <literal + role="hg-ext">patchbomb</literal> extension takes just one or + two lines in your <filename role="special"> + /.hgrc</filename>.</para> + <programlisting>[extensions] +patchbomb =</programlisting> + <para id="x_534">Once you've enabled the extension, you will have a new + command available, named <command + role="hg-ext-patchbomb">email</command>.</para> + + <para id="x_535">The safest and best way to invoke the <command + role="hg-ext-patchbomb">email</command> command is to + <emphasis>always</emphasis> run it first with the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -n</option> option. + This will show you what the command <emphasis>would</emphasis> + send, without actually sending anything. Once you've had a + quick glance over the changes and verified that you are sending + the right ones, you can rerun the same command, with the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -n</option> option + removed.</para> + + <para id="x_536">The <command role="hg-ext-patchbomb">email</command> command + accepts the same kind of revision syntax as every other + Mercurial command. For example, this command will send every + revision between 7 and <literal>tip</literal>, inclusive.</para> + <programlisting>hg email -n 7:tip</programlisting> + <para id="x_537">You can also specify a <emphasis>repository</emphasis> to + compare with. If you provide a repository but no revisions, the + <command role="hg-ext-patchbomb">email</command> command will + send all revisions in the local repository that are not present + in the remote repository. If you additionally specify revisions + or a branch name (the latter using the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -b</option> option), + this will constrain the revisions sent.</para> + + <para id="x_538">It's perfectly safe to run the <command + role="hg-ext-patchbomb">email</command> command without the + names of the people you want to send to: if you do this, it will + just prompt you for those values interactively. (If you're + using a Linux or Unix-like system, you should have enhanced + <literal>readline</literal>-style editing capabilities when + entering those headers, too, which is useful.)</para> + + <para id="x_539">When you are sending just one revision, the <command + role="hg-ext-patchbomb">email</command> command will by + default use the first line of the changeset description as the + subject of the single email message it sends.</para> + + <para id="x_53a">If you send multiple revisions, the <command + role="hg-ext-patchbomb">email</command> command will usually + send one message per changeset. It will preface the series with + an introductory message, in which you should describe the + purpose of the series of changes you're sending.</para> + + <sect2> + <title>Changing the behavior of patchbombs</title> + + <para id="x_53b">Not every project has exactly the same conventions for + sending changes in email; the <literal + role="hg-ext">patchbomb</literal> extension tries to + accommodate a number of variations through command line + options.</para> + <itemizedlist> + <listitem><para id="x_53c">You can write a subject for the introductory + message on the command line using the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -s</option> + option. This takes one argument, the text of the subject + to use.</para> + </listitem> + <listitem><para id="x_53d">To change the email address from which the + messages originate, use the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -f</option> + option. This takes one argument, the email address to + use.</para> + </listitem> + <listitem><para id="x_53e">The default behavior is to send unified diffs + (see <xref linkend="sec:mq:patch"/> for a + description of the + format), one per message. You can send a binary bundle + instead with the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -b</option> + option.</para> + </listitem> + <listitem><para id="x_53f">Unified diffs are normally prefaced with a + metadata header. You can omit this, and send unadorned + diffs, with the <option + role="hg-ext-patchbomb-cmd-email-opt">hg + --plain</option> option.</para> + </listitem> + <listitem><para id="x_540">Diffs are normally sent <quote>inline</quote>, + in the same body part as the description of a patch. This + makes it easiest for the largest number of readers to + quote and respond to parts of a diff, as some mail clients + will only quote the first MIME body part in a message. If + you'd prefer to send the description and the diff in + separate body parts, use the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -a</option> + option.</para> + </listitem> + <listitem><para id="x_541">Instead of sending mail messages, you can + write them to an <literal>mbox</literal>-format mail + folder using the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -m</option> + option. That option takes one argument, the name of the + file to write to.</para> + </listitem> + <listitem><para id="x_542">If you would like to add a + <command>diffstat</command>-format summary to each patch, + and one to the introductory message, use the <option + role="hg-ext-patchbomb-cmd-email-opt">hg -d</option> + option. The <command>diffstat</command> command displays + a table containing the name of each file patched, the + number of lines affected, and a histogram showing how much + each file is modified. This gives readers a qualitative + glance at how complex a patch is.</para> + </listitem></itemizedlist> + + </sect2> + </sect1> </chapter> <!-- local variables: sgml-parent-document: ("00book.xml" "book" "chapter") end: ---> \ No newline at end of file +-->