Mercurial > hgbook
changeset 749:7e7c47481e4f
Oops, this is the real merge for my hg's oddity
author | Dongsheng Song <dongsheng.song@gmail.com> |
---|---|
date | Fri, 20 Mar 2009 16:43:35 +0800 |
parents | d13c7c706a58 |
children | c15e039d98b8 |
files | .hgignore en/00book.xml en/Makefile en/appA-cmdref.xml en/appB-mq-ref.xml en/appC-srcinstall.xml en/appD-license.xml en/autoid.py en/ch00-preface.xml en/ch01-intro.xml en/ch01-tour-basic.xml en/ch02-tour-basic.xml en/ch02-tour-merge.xml en/ch03-concepts.xml en/ch03-tour-merge.xml en/ch04-concepts.xml en/ch04-daily.xml en/ch05-collab.xml en/ch05-daily.xml en/ch06-collab.xml en/ch06-filenames.xml en/ch07-branch.xml en/ch07-filenames.xml en/ch08-branch.xml en/ch08-undo.xml en/ch09-hook.xml en/ch09-undo.xml en/ch10-hook.xml en/ch10-template.xml en/ch11-mq.xml en/ch11-template.xml en/ch12-mq-collab.xml en/ch12-mq.xml en/ch13-hgext.xml en/ch13-mq-collab.xml en/ch14-hgext.xml po/zh.po web/hgbook/dbutil.py web/hgbook/load_elements.py |
diffstat | 39 files changed, 14077 insertions(+), 14895 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Fri Mar 20 15:40:06 2009 +0800 +++ b/.hgignore Fri Mar 20 16:43:35 2009 +0800 @@ -19,6 +19,7 @@ .\#* .run .validated-00book.xml +en/all-ids.dat web/hgbook/.database.sqlite3 web/hgbook/secrets.py stylesheets/system-xsl
--- a/en/00book.xml Fri Mar 20 15:40:06 2009 +0800 +++ b/en/00book.xml Fri Mar 20 16:43:35 2009 +0800 @@ -7,20 +7,20 @@ <!-- Chapters. --> -<!ENTITY ch01 SYSTEM "ch01-intro.xml"> -<!ENTITY ch02 SYSTEM "ch02-tour-basic.xml"> -<!ENTITY ch03 SYSTEM "ch03-tour-merge.xml"> -<!ENTITY ch04 SYSTEM "ch04-concepts.xml"> -<!ENTITY ch05 SYSTEM "ch05-daily.xml"> -<!ENTITY ch06 SYSTEM "ch06-collab.xml"> -<!ENTITY ch07 SYSTEM "ch07-filenames.xml"> -<!ENTITY ch08 SYSTEM "ch08-branch.xml"> -<!ENTITY ch09 SYSTEM "ch09-undo.xml"> -<!ENTITY ch10 SYSTEM "ch10-hook.xml"> -<!ENTITY ch11 SYSTEM "ch11-template.xml"> -<!ENTITY ch12 SYSTEM "ch12-mq.xml"> -<!ENTITY ch13 SYSTEM "ch13-mq-collab.xml"> -<!ENTITY ch14 SYSTEM "ch14-hgext.xml"> +<!ENTITY ch00 SYSTEM "ch00-preface.xml"> +<!ENTITY ch01 SYSTEM "ch01-tour-basic.xml"> +<!ENTITY ch02 SYSTEM "ch02-tour-merge.xml"> +<!ENTITY ch03 SYSTEM "ch03-concepts.xml"> +<!ENTITY ch04 SYSTEM "ch04-daily.xml"> +<!ENTITY ch05 SYSTEM "ch05-collab.xml"> +<!ENTITY ch06 SYSTEM "ch06-filenames.xml"> +<!ENTITY ch07 SYSTEM "ch07-branch.xml"> +<!ENTITY ch08 SYSTEM "ch08-undo.xml"> +<!ENTITY ch09 SYSTEM "ch09-hook.xml"> +<!ENTITY ch10 SYSTEM "ch10-template.xml"> +<!ENTITY ch11 SYSTEM "ch11-mq.xml"> +<!ENTITY ch12 SYSTEM "ch12-mq-collab.xml"> +<!ENTITY ch13 SYSTEM "ch13-hgext.xml"> <!ENTITY appA SYSTEM "appA-cmdref.xml"> <!ENTITY appB SYSTEM "appB-mq-ref.xml"> <!ENTITY appC SYSTEM "appC-srcinstall.xml"> @@ -79,7 +79,6 @@ &ch11; &ch12; &ch13; - &ch14; <!-- &appA; --> &appB; &appC;
--- a/en/Makefile Fri Mar 20 15:40:06 2009 +0800 +++ b/en/Makefile Fri Mar 20 16:43:35 2009 +0800 @@ -130,21 +130,24 @@ all: web -../stylesheets/system-xsl: $(system-xsl-dir) +../xsl/system-xsl: $(system-xsl-dir) ln -s $< $@ web: ../xsl/system-xsl websup html -html: ../stylesheets/system-xsl $(xml-src-files) valid - xsltproc $(xsltproc-opts) -o html/read/x ../stylesheets/chunk-stylesheet.xsl 00book.xml +html: ../xsl/system-xsl $(xml-src-files) valid + xsltproc $(xsltproc-opts) -o html/read/x ../xsl/chunk-stylesheet.xsl 00book.xml for i in html/read/*.html; do \ gzip -9 -c $$i > $$i.gz; \ done websup: $(extras-web) - mkdir -p $(obj-websup)/images - cp ../stylesheets/system-xsl/images/*.png $(obj-websup)/images - cp -f ../web/icons/*.png $(obj-websup)/images + mkdir -p $(obj-websup)/figs + cp ../xsl/system-xsl/images/*.png $(obj-websup)/figs + cp -f ../web/icons/*.png $(obj-websup)/figs + +all-ids.dat: ../xsl/all-ids.xsl $(xml-src-files) + $(xsltproc) $(xsltproc-opts) -o $@ ../xsl/all-ids.xsl 00book.xml web: websup
--- a/en/appA-cmdref.xml Fri Mar 20 15:40:06 2009 +0800 +++ b/en/appA-cmdref.xml Fri Mar 20 16:43:35 2009 +0800 @@ -3,113 +3,113 @@ <appendix id="cmdref"> <title>Command reference</title> -<para>\cmdref{add}{add files at the next commit} +<para id="x_653">\cmdref{add}{add files at the next commit} \optref{add}{I}{include} \optref{add}{X}{exclude} \optref{add}{n}{dry-run}</para> -<para>\cmdref{diff}{print changes in history or working directory}</para> +<para id="x_654">\cmdref{diff}{print changes in history or working directory}</para> -<para>Show differences between revisions for the specified files or +<para id="x_655">Show differences between revisions for the specified files or directories, using the unified diff format. For a description of the unified diff format, see section <xref linkend="sec.mq.patch"/>.</para> -<para>By default, this command does not print diffs for files that Mercurial +<para id="x_656">By default, this command does not print diffs for files that Mercurial considers to contain binary data. To control this behaviour, see the <option role="hg-opt-diff">-a</option> and <option role="hg-opt-diff">--git</option> options.</para> <sect2> <title>Options</title> -<para>\loptref{diff}{nodates}</para> +<para id="x_657">\loptref{diff}{nodates}</para> -<para>Omit date and time information when printing diff headers.</para> +<para id="x_658">Omit date and time information when printing diff headers.</para> -<para>\optref{diff}{B}{ignore-blank-lines}</para> +<para id="x_659">\optref{diff}{B}{ignore-blank-lines}</para> -<para>Do not print changes that only insert or delete blank lines. A line +<para id="x_65a">Do not print changes that only insert or delete blank lines. A line that contains only whitespace is not considered blank. </para> -<para>\optref{diff}{I}{include} +<para id="x_65b">\optref{diff}{I}{include} </para> -<para>Include files and directories whose names match the given patterns. +<para id="x_65c">Include files and directories whose names match the given patterns. </para> -<para>\optref{diff}{X}{exclude} +<para id="x_65d">\optref{diff}{X}{exclude} </para> -<para>Exclude files and directories whose names match the given patterns. +<para id="x_65e">Exclude files and directories whose names match the given patterns. </para> -<para>\optref{diff}{a}{text} +<para id="x_65f">\optref{diff}{a}{text} </para> -<para>If this option is not specified, <command role="hg-cmd">hg diff</command> will refuse to print +<para id="x_660">If this option is not specified, <command role="hg-cmd">hg diff</command> will refuse to print diffs for files that it detects as binary. Specifying <option role="hg-opt-diff">-a</option> forces <command role="hg-cmd">hg diff</command> to treat all files as text, and generate diffs for all of them. </para> -<para>This option is useful for files that are <quote>mostly text</quote> but have a +<para id="x_661">This option is useful for files that are <quote>mostly text</quote> but have a few embedded NUL characters. If you use it on files that contain a lot of binary data, its output will be incomprehensible. </para> -<para>\optref{diff}{b}{ignore-space-change} +<para id="x_662">\optref{diff}{b}{ignore-space-change} </para> -<para>Do not print a line if the only change to that line is in the amount +<para id="x_663">Do not print a line if the only change to that line is in the amount of white space it contains. </para> -<para>\optref{diff}{g}{git} +<para id="x_664">\optref{diff}{g}{git} </para> -<para>Print <command>git</command>-compatible diffs. XXX reference a format +<para id="x_665">Print <command>git</command>-compatible diffs. XXX reference a format description. </para> -<para>\optref{diff}{p}{show-function} +<para id="x_666">\optref{diff}{p}{show-function} </para> -<para>Display the name of the enclosing function in a hunk header, using a +<para id="x_667">Display the name of the enclosing function in a hunk header, using a simple heuristic. This functionality is enabled by default, so the <option role="hg-opt-diff">-p</option> option has no effect unless you change the value of the <envar role="rc-item-diff">showfunc</envar> config item, as in the following example.</para> <!-- &interaction.cmdref.diff-p; --> -<para>\optref{diff}{r}{rev} +<para id="x_668">\optref{diff}{r}{rev} </para> -<para>Specify one or more revisions to compare. The <command role="hg-cmd">hg diff</command> command +<para id="x_669">Specify one or more revisions to compare. The <command role="hg-cmd">hg diff</command> command accepts up to two <option role="hg-opt-diff">-r</option> options to specify the revisions to compare. </para> <orderedlist> -<listitem><para>Display the differences between the parent revision of the +<listitem><para id="x_66a">Display the differences between the parent revision of the working directory and the working directory. </para> </listitem> -<listitem><para>Display the differences between the specified changeset and the +<listitem><para id="x_66b">Display the differences between the specified changeset and the working directory. </para> </listitem> -<listitem><para>Display the differences between the two specified changesets. +<listitem><para id="x_66c">Display the differences between the two specified changesets. </para> </listitem></orderedlist> -<para>You can specify two revisions using either two <option role="hg-opt-diff">-r</option> +<para id="x_66d">You can specify two revisions using either two <option role="hg-opt-diff">-r</option> options or revision range notation. For example, the two revision specifications below are equivalent. </para> <programlisting>hg diff -r 10 -r 20 hg diff -r10:20</programlisting> -<para>When you provide two revisions, Mercurial treats the order of those +<para id="x_66e">When you provide two revisions, Mercurial treats the order of those revisions as significant. Thus, <command role="hg-cmd">hg diff -r10:20</command> will produce a diff that will transform files from their contents as of revision 10 to their contents as of revision 20, while @@ -119,23 +119,23 @@ diffing against the working directory. </para> -<para>\optref{diff}{w}{ignore-all-space} +<para id="x_66f">\optref{diff}{w}{ignore-all-space} </para> -<para>\cmdref{version}{print version and copyright information} +<para id="x_670">\cmdref{version}{print version and copyright information} </para> -<para>This command displays the version of Mercurial you are running, and +<para id="x_671">This command displays the version of Mercurial you are running, and its copyright license. There are four kinds of version string that you may see. </para> <itemizedlist> -<listitem><para>The string <quote><literal>unknown</literal></quote>. This version of Mercurial was +<listitem><para id="x_672">The string <quote><literal>unknown</literal></quote>. This version of Mercurial was not built in a Mercurial repository, and cannot determine its own version. </para> </listitem> -<listitem><para>A short numeric string, such as <quote><literal>1.1</literal></quote>. This is a +<listitem><para id="x_673">A short numeric string, such as <quote><literal>1.1</literal></quote>. This is a build of a revision of Mercurial that was identified by a specific tag in the repository where it was built. (This doesn't necessarily mean that you're running an official release; someone else could @@ -143,11 +143,11 @@ built Mercurial.) </para> </listitem> -<listitem><para>A hexadecimal string, such as <quote><literal>875489e31abe</literal></quote>. This +<listitem><para id="x_674">A hexadecimal string, such as <quote><literal>875489e31abe</literal></quote>. This is a build of the given revision of Mercurial. </para> </listitem> -<listitem><para>A hexadecimal string followed by a date, such as +<listitem><para id="x_675">A hexadecimal string followed by a date, such as <quote><literal>875489e31abe+20070205</literal></quote>. This is a build of the given revision of Mercurial, where the build repository contained some local changes that had not been committed. @@ -161,14 +161,14 @@ <sect3 id="cmdref.diff-vs-status"> <title>Why do the results of <command role="hg-cmd">hg diff</command> and <command role="hg-cmd">hg status</command> differ?</title> -<para>When you run the <command role="hg-cmd">hg status</command> command, you'll see a list of files +<para id="x_676">When you run the <command role="hg-cmd">hg status</command> command, you'll see a list of files that Mercurial will record changes for the next time you perform a commit. If you run the <command role="hg-cmd">hg diff</command> command, you may notice that it prints diffs for only a <emphasis>subset</emphasis> of the files that <command role="hg-cmd">hg status</command> listed. There are two possible reasons for this. </para> -<para>The first is that <command role="hg-cmd">hg status</command> prints some kinds of modifications +<para id="x_677">The first is that <command role="hg-cmd">hg status</command> prints some kinds of modifications that <command role="hg-cmd">hg diff</command> doesn't normally display. The <command role="hg-cmd">hg diff</command> command normally outputs unified diffs, which don't have the ability to represent some changes that Mercurial can track. Most notably, @@ -176,12 +176,12 @@ executable, but Mercurial records this information. </para> -<para>If you use the <option role="hg-opt-diff">--git</option> option to <command role="hg-cmd">hg diff</command>, it will +<para id="x_678">If you use the <option role="hg-opt-diff">--git</option> option to <command role="hg-cmd">hg diff</command>, it will display <command>git</command>-compatible diffs that <emphasis>can</emphasis> display this extra information. </para> -<para>The second possible reason that <command role="hg-cmd">hg diff</command> might be printing diffs +<para id="x_679">The second possible reason that <command role="hg-cmd">hg diff</command> might be printing diffs for a subset of the files displayed by <command role="hg-cmd">hg status</command> is that if you invoke it without any arguments, <command role="hg-cmd">hg diff</command> prints diffs against the first parent of the working directory. If you have run <command role="hg-cmd">hg merge</command> @@ -199,14 +199,14 @@ <sect3> <title>Generating safe binary diffs</title> -<para>If you use the <option role="hg-opt-diff">-a</option> option to force Mercurial to print +<para id="x_67a">If you use the <option role="hg-opt-diff">-a</option> option to force Mercurial to print diffs of files that are either <quote>mostly text</quote> or contain lots of binary data, those diffs cannot subsequently be applied by either Mercurial's <command role="hg-cmd">hg import</command> command or the system's <command>patch</command> command. </para> -<para>If you want to generate a diff of a binary file that is safe to use as +<para id="x_67b">If you want to generate a diff of a binary file that is safe to use as input for <command role="hg-cmd">hg import</command>, use the <command role="hg-cmd">hg diff</command>{--git} option when you generate the patch. The system <command>patch</command> command cannot handle binary patches at all.
--- a/en/appB-mq-ref.xml Fri Mar 20 15:40:06 2009 +0800 +++ b/en/appB-mq-ref.xml Fri Mar 20 16:43:35 2009 +0800 @@ -7,14 +7,14 @@ <sect1 id="sec.mqref.cmdref"> <title>MQ command reference</title> - <para>For an overview of the commands provided by MQ, use the + <para id="x_5e8">For an overview of the commands provided by MQ, use the command <command role="hg-cmd">hg help mq</command>.</para> <sect2> <title><command role="hg-ext-mq">qapplied</command>&emdash;print applied patches</title> - <para>The <command role="hg-ext-mq">qapplied</command> command + <para id="x_5e9">The <command role="hg-ext-mq">qapplied</command> command prints the current stack of applied patches. Patches are printed in oldest-to-newest order, so the last patch in the list is the <quote>top</quote> patch.</para> @@ -24,7 +24,7 @@ <title><command role="hg-ext-mq">qcommit</command>&emdash;commit changes in the queue repository</title> - <para>The <command role="hg-ext-mq">qcommit</command> command + <para id="x_5ea">The <command role="hg-ext-mq">qcommit</command> command commits any outstanding changes in the <filename role="special" class="directory">.hg/patches</filename> repository. This command only works if the <filename @@ -36,7 +36,7 @@ after running <command role="hg-ext-mq">qinit</command>.</para> - <para>This command is shorthand for <command role="hg-cmd">hg + <para id="x_5eb">This command is shorthand for <command role="hg-cmd">hg commit --cwd .hg/patches</command>.</para> </sect2> <sect2> @@ -45,7 +45,7 @@ from the <filename role="special">series</filename> file}</title> - <para>The <command role="hg-ext-mq">qdelete</command> command + <para id="x_5ec">The <command role="hg-ext-mq">qdelete</command> command removes the entry for a patch from the <filename role="special">series</filename> file in the <filename role="special" class="directory">.hg/patches</filename> @@ -54,9 +54,9 @@ the <option role="hg-ext-mq-cmd-qdel-opt">-f</option> option to do that.</para> - <para>Options:</para> + <para id="x_5ed">Options:</para> <itemizedlist> - <listitem><para><option + <listitem><para id="x_5ee"><option role="hg-ext-mq-cmd-qdel-opt">-f</option>: Delete the patch file.</para> </listitem></itemizedlist> @@ -66,7 +66,7 @@ <title><command role="hg-ext-mq">qdiff</command>&emdash;print a diff of the topmost applied patch</title> - <para>The <command role="hg-ext-mq">qdiff</command> command + <para id="x_5ef">The <command role="hg-ext-mq">qdiff</command> command prints a diff of the topmost applied patch. It is equivalent to <command role="hg-cmd">hg diff -r-2:-1</command>.</para> @@ -75,12 +75,12 @@ <title><command role="hg-ext-mq">qfold</command>&emdash;merge (<quote>fold</quote>) several patches into one</title> - <para>The <command role="hg-ext-mq">qfold</command> command + <para id="x_5f0">The <command role="hg-ext-mq">qfold</command> command merges multiple patches into the topmost applied patch, so that the topmost applied patch makes the union of all of the changes in the patches in question.</para> - <para>The patches to fold must not be applied; <command + <para id="x_5f1">The patches to fold must not be applied; <command role="hg-ext-mq">qfold</command> will exit with an error if any is. The order in which patches are folded is significant; <command role="hg-cmd">hg qfold a b</command> means @@ -88,7 +88,7 @@ <literal>a</literal>, followed by <literal>b</literal></quote>.</para> - <para>The comments from the folded patches are appended to the + <para id="x_5f2">The comments from the folded patches are appended to the comments of the destination patch, with each block of comments separated by three asterisk (<quote><literal>*</literal></quote>) characters. Use the @@ -96,19 +96,19 @@ edit the commit message for the combined patch/changeset after the folding has completed.</para> - <para>Options:</para> + <para id="x_5f3">Options:</para> <itemizedlist> - <listitem><para><option + <listitem><para id="x_5f4"><option role="hg-ext-mq-cmd-qfold-opt">-e</option>: Edit the commit message and patch description for the newly folded patch.</para> </listitem> - <listitem><para><option + <listitem><para id="x_5f5"><option role="hg-ext-mq-cmd-qfold-opt">-l</option>: Use the contents of the given file as the new commit message and patch description for the folded patch.</para> </listitem> - <listitem><para><option + <listitem><para id="x_5f6"><option role="hg-ext-mq-cmd-qfold-opt">-m</option>: Use the given text as the new commit message and patch description for the folded patch.</para> @@ -120,7 +120,7 @@ role="hg-ext-mq">qheader</command>&emdash;display the header/description of a patch</title> - <para>The <command role="hg-ext-mq">qheader</command> command + <para id="x_5f7">The <command role="hg-ext-mq">qheader</command> command prints the header, or description, of a patch. By default, it prints the header of the topmost applied patch. Given an argument, it prints the header of the named patch.</para> @@ -130,7 +130,7 @@ <title><command role="hg-ext-mq">qimport</command>&emdash;import a third-party patch into the queue</title> - <para>The <command role="hg-ext-mq">qimport</command> command + <para id="x_5f8">The <command role="hg-ext-mq">qimport</command> command adds an entry for an external patch to the <filename role="special">series</filename> file, and copies the patch into the <filename role="special" @@ -138,7 +138,7 @@ the entry immediately after the topmost applied patch, but does not push the patch.</para> - <para>If the <filename role="special" + <para id="x_5f9">If the <filename role="special" class="directory">.hg/patches</filename> directory is a repository, <command role="hg-ext-mq">qimport</command> automatically does an <command role="hg-cmd">hg add</command> @@ -149,14 +149,14 @@ <title><command role="hg-ext-mq">qinit</command>&emdash;prepare a repository to work with MQ</title> - <para>The <command role="hg-ext-mq">qinit</command> command + <para id="x_5fa">The <command role="hg-ext-mq">qinit</command> command prepares a repository to work with MQ. It creates a directory called <filename role="special" class="directory">.hg/patches</filename>.</para> - <para>Options:</para> + <para id="x_5fb">Options:</para> <itemizedlist> - <listitem><para><option + <listitem><para id="x_5fc"><option role="hg-ext-mq-cmd-qinit-opt">-c</option>: Create <filename role="special" class="directory">.hg/patches</filename> as a repository @@ -166,7 +166,7 @@ file.</para> </listitem></itemizedlist> - <para>When the <filename role="special" + <para id="x_5fd">When the <filename role="special" class="directory">.hg/patches</filename> directory is a repository, the <command role="hg-ext-mq">qimport</command> and <command role="hg-ext-mq">qnew</command> commands @@ -178,7 +178,7 @@ <title><command role="hg-ext-mq">qnew</command>&emdash;create a new patch</title> - <para>The <command role="hg-ext-mq">qnew</command> command + <para id="x_5fe">The <command role="hg-ext-mq">qnew</command> command creates a new patch. It takes one mandatory argument, the name to use for the patch file. The newly created patch is created empty by default. It is added to the <filename @@ -186,7 +186,7 @@ topmost applied patch, and is immediately pushed on top of that patch.</para> - <para>If <command role="hg-ext-mq">qnew</command> finds modified + <para id="x_5ff">If <command role="hg-ext-mq">qnew</command> finds modified files in the working directory, it will refuse to create a new patch unless the <option role="hg-ext-mq-cmd-qnew-opt">-f</option> option is used @@ -194,16 +194,16 @@ role="hg-ext-mq">qrefresh</command> your topmost applied patch before you apply a new patch on top of it.</para> - <para>Options:</para> + <para id="x_600">Options:</para> <itemizedlist> - <listitem><para><option + <listitem><para id="x_601"><option role="hg-ext-mq-cmd-qnew-opt">-f</option>: Create a new patch if the contents of the working directory are modified. Any outstanding modifications are added to the newly created patch, so after this command completes, the working directory will no longer be modified.</para> </listitem> - <listitem><para><option + <listitem><para id="x_602"><option role="hg-ext-mq-cmd-qnew-opt">-m</option>: Use the given text as the commit message. This text will be stored at the beginning of the patch file, before the patch @@ -215,7 +215,7 @@ <title><command role="hg-ext-mq">qnext</command>&emdash;print the name of the next patch</title> - <para>The <command role="hg-ext-mq">qnext</command> command + <para id="x_603">The <command role="hg-ext-mq">qnext</command> command prints the name name of the next patch in the <filename role="special">series</filename> file after the topmost applied patch. This patch will become the topmost applied @@ -227,15 +227,15 @@ <title><command role="hg-ext-mq">qpop</command>&emdash;pop patches off the stack</title> - <para>The <command role="hg-ext-mq">qpop</command> command + <para id="x_604">The <command role="hg-ext-mq">qpop</command> command removes applied patches from the top of the stack of applied patches. By default, it removes only one patch.</para> - <para>This command removes the changesets that represent the + <para id="x_605">This command removes the changesets that represent the popped patches from the repository, and updates the working directory to undo the effects of the patches.</para> - <para>This command takes an optional argument, which it uses as + <para id="x_606">This command takes an optional argument, which it uses as the name or index of the patch to pop to. If given a name, it will pop patches until the named patch is the topmost applied patch. If given a number, <command @@ -245,7 +245,7 @@ It pops patches until the patch identified by the given index is the topmost applied patch.</para> - <para>The <command role="hg-ext-mq">qpop</command> command does + <para id="x_607">The <command role="hg-ext-mq">qpop</command> command does not read or write patches or the <filename role="special">series</filename> file. It is thus safe to <command role="hg-ext-mq">qpop</command> a patch that you have @@ -254,31 +254,31 @@ In the latter two cases, use the name of the patch as it was when you applied it.</para> - <para>By default, the <command role="hg-ext-mq">qpop</command> + <para id="x_608">By default, the <command role="hg-ext-mq">qpop</command> command will not pop any patches if the working directory has been modified. You can override this behaviour using the <option role="hg-ext-mq-cmd-qpop-opt">-f</option> option, which reverts all modifications in the working directory.</para> - <para>Options:</para> + <para id="x_609">Options:</para> <itemizedlist> - <listitem><para><option + <listitem><para id="x_60a"><option role="hg-ext-mq-cmd-qpop-opt">-a</option>: Pop all applied patches. This returns the repository to its state before you applied any patches.</para> </listitem> - <listitem><para><option + <listitem><para id="x_60b"><option role="hg-ext-mq-cmd-qpop-opt">-f</option>: Forcibly revert any modifications to the working directory when popping.</para> </listitem> - <listitem><para><option + <listitem><para id="x_60c"><option role="hg-ext-mq-cmd-qpop-opt">-n</option>: Pop a patch from the named queue.</para> </listitem></itemizedlist> - <para>The <command role="hg-ext-mq">qpop</command> command + <para id="x_60d">The <command role="hg-ext-mq">qpop</command> command removes one line from the end of the <filename role="special">status</filename> file for each patch that it pops.</para> @@ -288,7 +288,7 @@ <title><command role="hg-ext-mq">qprev</command>&emdash;print the name of the previous patch</title> - <para>The <command role="hg-ext-mq">qprev</command> command + <para id="x_60e">The <command role="hg-ext-mq">qprev</command> command prints the name of the patch in the <filename role="special">series</filename> file that comes before the topmost applied patch. This will become the topmost applied @@ -300,18 +300,18 @@ <title><command role="hg-ext-mq">qpush</command>&emdash;push patches onto the stack</title> - <para>The <command role="hg-ext-mq">qpush</command> command adds + <para id="x_60f">The <command role="hg-ext-mq">qpush</command> command adds patches onto the applied stack. By default, it adds only one patch.</para> - <para>This command creates a new changeset to represent each + <para id="x_610">This command creates a new changeset to represent each applied patch, and updates the working directory to apply the effects of the patches.</para> - <para>The default data used when creating a changeset are as + <para id="x_611">The default data used when creating a changeset are as follows:</para> <itemizedlist> - <listitem><para>The commit date and time zone are the current + <listitem><para id="x_612">The commit date and time zone are the current date and time zone. Because these data are used to compute the identity of a changeset, this means that if you <command role="hg-ext-mq">qpop</command> a patch and @@ -319,32 +319,32 @@ changeset that you push will have a different identity than the changeset you popped.</para> </listitem> - <listitem><para>The author is the same as the default used by + <listitem><para id="x_613">The author is the same as the default used by the <command role="hg-cmd">hg commit</command> command.</para> </listitem> - <listitem><para>The commit message is any text from the patch + <listitem><para id="x_614">The commit message is any text from the patch file that comes before the first diff header. If there is no such text, a default commit message is used that identifies the name of the patch.</para> </listitem></itemizedlist> - <para>If a patch contains a Mercurial patch header (XXX add + <para id="x_615">If a patch contains a Mercurial patch header (XXX add link), the information in the patch header overrides these defaults.</para> - <para>Options:</para> + <para id="x_616">Options:</para> <itemizedlist> - <listitem><para><option + <listitem><para id="x_617"><option role="hg-ext-mq-cmd-qpush-opt">-a</option>: Push all unapplied patches from the <filename role="special">series</filename> file until there are none left to push.</para> </listitem> - <listitem><para><option + <listitem><para id="x_618"><option role="hg-ext-mq-cmd-qpush-opt">-l</option>: Add the name of the patch to the end of the commit message.</para> </listitem> - <listitem><para><option + <listitem><para id="x_619"><option role="hg-ext-mq-cmd-qpush-opt">-m</option>: If a patch fails to apply cleanly, use the entry for the patch in another saved queue to compute the parameters for a @@ -352,12 +352,12 @@ normal Mercurial merge machinery. Use the resolution of the merge as the new patch content.</para> </listitem> - <listitem><para><option + <listitem><para id="x_61a"><option role="hg-ext-mq-cmd-qpush-opt">-n</option>: Use the named queue if merging while pushing.</para> </listitem></itemizedlist> - <para>The <command role="hg-ext-mq">qpush</command> command + <para id="x_61b">The <command role="hg-ext-mq">qpush</command> command reads, but does not modify, the <filename role="special">series</filename> file. It appends one line to the <command role="hg-cmd">hg status</command> file for @@ -369,24 +369,24 @@ role="hg-ext-mq">qrefresh</command>&emdash;update the topmost applied patch</title> - <para>The <command role="hg-ext-mq">qrefresh</command> command + <para id="x_61c">The <command role="hg-ext-mq">qrefresh</command> command updates the topmost applied patch. It modifies the patch, removes the old changeset that represented the patch, and creates a new changeset to represent the modified patch.</para> - <para>The <command role="hg-ext-mq">qrefresh</command> command + <para id="x_61d">The <command role="hg-ext-mq">qrefresh</command> command looks for the following modifications:</para> <itemizedlist> - <listitem><para>Changes to the commit message, i.e. the text + <listitem><para id="x_61e">Changes to the commit message, i.e. the text before the first diff header in the patch file, are reflected in the new changeset that represents the patch.</para> </listitem> - <listitem><para>Modifications to tracked files in the working + <listitem><para id="x_61f">Modifications to tracked files in the working directory are added to the patch.</para> </listitem> - <listitem><para>Changes to the files tracked using <command + <listitem><para id="x_620">Changes to the files tracked using <command role="hg-cmd">hg add</command>, <command role="hg-cmd">hg copy</command>, <command role="hg-cmd">hg remove</command>, or <command @@ -395,25 +395,25 @@ removed files and rename sources are removed.</para> </listitem></itemizedlist> - <para>Even if <command role="hg-ext-mq">qrefresh</command> + <para id="x_621">Even if <command role="hg-ext-mq">qrefresh</command> detects no changes, it still recreates the changeset that represents the patch. This causes the identity of the changeset to differ from the previous changeset that identified the patch.</para> - <para>Options:</para> + <para id="x_622">Options:</para> <itemizedlist> - <listitem><para><option + <listitem><para id="x_623"><option role="hg-ext-mq-cmd-qrefresh-opt">-e</option>: Modify the commit and patch description, using the preferred text editor.</para> </listitem> - <listitem><para><option + <listitem><para id="x_624"><option role="hg-ext-mq-cmd-qrefresh-opt">-m</option>: Modify the commit message and patch description, using the given text.</para> </listitem> - <listitem><para><option + <listitem><para id="x_625"><option role="hg-ext-mq-cmd-qrefresh-opt">-l</option>: Modify the commit message and patch description, using text from the given file.</para> @@ -424,11 +424,11 @@ <title><command role="hg-ext-mq">qrename</command>&emdash;rename a patch</title> - <para>The <command role="hg-ext-mq">qrename</command> command + <para id="x_626">The <command role="hg-ext-mq">qrename</command> command renames a patch, and changes the entry for the patch in the <filename role="special">series</filename> file.</para> - <para>With a single argument, <command + <para id="x_627">With a single argument, <command role="hg-ext-mq">qrename</command> renames the topmost applied patch. With two arguments, it renames its first argument to its second.</para> @@ -439,21 +439,21 @@ role="hg-ext-mq">qrestore</command>&emdash;restore saved queue state</title> - <para>XXX No idea what this does.</para> + <para id="x_628">XXX No idea what this does.</para> </sect2> <sect2> <title><command role="hg-ext-mq">qsave</command>&emdash;save current queue state</title> - <para>XXX Likewise.</para> + <para id="x_629">XXX Likewise.</para> </sect2> <sect2> <title><command role="hg-ext-mq">qseries</command>&emdash;print the entire patch series</title> - <para>The <command role="hg-ext-mq">qseries</command> command + <para id="x_62a">The <command role="hg-ext-mq">qseries</command> command prints the entire patch series from the <filename role="special">series</filename> file. It prints only patch names, not empty lines or comments. It prints in order from @@ -464,7 +464,7 @@ <title><command role="hg-ext-mq">qtop</command>&emdash;print the name of the current patch</title> - <para>The <command role="hg-ext-mq">qtop</command> prints the + <para id="x_62b">The <command role="hg-ext-mq">qtop</command> prints the name of the topmost currently applied patch.</para> </sect2> @@ -473,7 +473,7 @@ role="hg-ext-mq">qunapplied</command>&emdash;print patches not yet applied</title> - <para>The <command role="hg-ext-mq">qunapplied</command> command + <para id="x_62c">The <command role="hg-ext-mq">qunapplied</command> command prints the names of patches from the <filename role="special">series</filename> file that are not yet applied. It prints them in order from the next patch that @@ -484,28 +484,28 @@ <title><command role="hg-cmd">hg strip</command>&emdash;remove a revision and descendants</title> - <para>The <command role="hg-cmd">hg strip</command> command + <para id="x_62d">The <command role="hg-cmd">hg strip</command> command removes a revision, and all of its descendants, from the repository. It undoes the effects of the removed revisions from the repository, and updates the working directory to the first parent of the removed revision.</para> - <para>The <command role="hg-cmd">hg strip</command> command + <para id="x_62e">The <command role="hg-cmd">hg strip</command> command saves a backup of the removed changesets in a bundle, so that they can be reapplied if removed in error.</para> - <para>Options:</para> + <para id="x_62f">Options:</para> <itemizedlist> - <listitem><para><option role="hg-opt-strip">-b</option>: Save + <listitem><para id="x_630"><option role="hg-opt-strip">-b</option>: Save unrelated changesets that are intermixed with the stripped changesets in the backup bundle.</para> </listitem> - <listitem><para><option role="hg-opt-strip">-f</option>: If a + <listitem><para id="x_631"><option role="hg-opt-strip">-f</option>: If a branch has multiple heads, remove all heads. XXX This should be renamed, and use <literal>-f</literal> to strip revs when there are pending changes.</para> </listitem> - <listitem><para><option role="hg-opt-strip">-n</option>: Do + <listitem><para id="x_632"><option role="hg-opt-strip">-n</option>: Do not save a backup bundle.</para> </listitem></itemizedlist> @@ -518,18 +518,18 @@ <title>The <filename role="special">series</filename> file</title> - <para>The <filename role="special">series</filename> file + <para id="x_633">The <filename role="special">series</filename> file contains a list of the names of all patches that MQ can apply. It is represented as a list of names, with one name saved per line. Leading and trailing white space in each line are ignored.</para> - <para>Lines may contain comments. A comment begins with the + <para id="x_634">Lines may contain comments. A comment begins with the <quote><literal>#</literal></quote> character, and extends to the end of the line. Empty lines, and lines that contain only comments, are ignored.</para> - <para>You will often need to edit the <filename + <para id="x_635">You will often need to edit the <filename role="special">series</filename> file by hand, hence the support for comments and empty lines noted above. For example, you can comment out a patch temporarily, and <command @@ -538,7 +538,7 @@ patches are applied by reordering their entries in the <filename role="special">series</filename> file.</para> - <para>Placing the <filename role="special">series</filename> + <para id="x_636">Placing the <filename role="special">series</filename> file under revision control is also supported; it is a good idea to place all of the patches that it refers to under revision control, as well. If you create a patch directory @@ -551,7 +551,7 @@ <title>The <filename role="special">status</filename> file</title> - <para>The <filename role="special">status</filename> file + <para id="x_637">The <filename role="special">status</filename> file contains the names and changeset hashes of all patches that MQ currently has applied. Unlike the <filename role="special">series</filename> file, this file is not
--- a/en/appC-srcinstall.xml Fri Mar 20 15:40:06 2009 +0800 +++ b/en/appC-srcinstall.xml Fri Mar 20 16:43:35 2009 +0800 @@ -7,29 +7,29 @@ <sect1 id="sec.srcinstall.unixlike"> <title>On a Unix-like system</title> - <para>If you are using a Unix-like system that has a sufficiently + <para id="x_5e0">If you are using a Unix-like system that has a sufficiently recent version of Python (2.3 or newer) available, it is easy to install Mercurial from source.</para> <orderedlist> - <listitem><para>Download a recent source tarball from <ulink + <listitem><para id="x_5e1">Download a recent source tarball from <ulink url="http://www.selenic.com/mercurial/download">http://www.selenic.com/mercurial/download</ulink>.</para> </listitem> - <listitem><para>Unpack the tarball:</para> + <listitem><para id="x_5e2">Unpack the tarball:</para> <programlisting>gzip -dc mercurial-MYVERSION.tar.gz | tar xf -</programlisting> </listitem> - <listitem><para>Go into the source directory and run the + <listitem><para id="x_5e3">Go into the source directory and run the installer script. This will build Mercurial and install it in your home directory.</para> <programlisting>cd mercurial-MYVERSION python setup.py install --force --home=$HOME</programlisting> </listitem> </orderedlist> - <para>Once the install finishes, Mercurial will be in the + <para id="x_5e4">Once the install finishes, Mercurial will be in the <literal>bin</literal> subdirectory of your home directory. Don't forget to make sure that this directory is present in your shell's search path.</para> - <para>You will probably need to set the <envar>PYTHONPATH</envar> + <para id="x_5e5">You will probably need to set the <envar>PYTHONPATH</envar> environment variable so that the Mercurial executable can find the rest of the Mercurial packages. For example, on my laptop, I have set it to <literal>/home/bos/lib/python</literal>. The @@ -43,14 +43,14 @@ <sect1> <title>On Windows</title> - <para>Building and installing Mercurial on Windows requires a + <para id="x_5e6">Building and installing Mercurial on Windows requires a variety of tools, a fair amount of technical knowledge, and considerable patience. I very much <emphasis>do not recommend</emphasis> this route if you are a <quote>casual user</quote>. Unless you intend to hack on Mercurial, I strongly suggest that you use a binary package instead.</para> - <para>If you are intent on building Mercurial from source on + <para id="x_5e7">If you are intent on building Mercurial from source on Windows, follow the <quote>hard way</quote> directions on the Mercurial wiki at <ulink url="http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall">http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall</ulink>,
--- a/en/appD-license.xml Fri Mar 20 15:40:06 2009 +0800 +++ b/en/appD-license.xml Fri Mar 20 16:43:35 2009 +0800 @@ -4,24 +4,24 @@ <?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> - <para>The Open Publication works may be reproduced and distributed + <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 + <para id="x_63a">Proper form for an incorporation by reference is as follows:</para> <blockquote> - <para> Copyright (c) <emphasis>year</emphasis> by + <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, @@ -30,14 +30,14 @@ url="http://www.opencontent.org/openpub/">http://www.opencontent.org/openpub/</ulink>).</para> </blockquote> - <para>The reference must be immediately followed with any options + <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 section <xref linkend="sec.opl.options"/>).</para> - <para>Commercial redistribution of Open Publication-licensed + <para id="x_63d">Commercial redistribution of Open Publication-licensed material is permitted.</para> - <para>Any publication in standard (paper) book form shall require + <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 @@ -48,30 +48,30 @@ <sect1> <title>Copyright</title> - <para>The copyright to each Open Publication is owned by its + <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> - <para>The following license terms apply to all Open Publication + <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 + <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 + <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 + <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 @@ -82,25 +82,25 @@ <sect1> <title>Requirements on modified works</title> - <para>All modified versions of documents covered by this license, + <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 + <listitem><para id="x_645">The modified version must be labeled as such.</para> </listitem> - <listitem><para>The person making the modifications must be + <listitem><para id="x_646">The person making the modifications must be identified and the modifications dated.</para> </listitem> - <listitem><para>Acknowledgement of the original author and + <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>The location of the original unmodified document + <listitem><para id="x_648">The location of the original unmodified document must be identified.</para> </listitem> - <listitem><para>The original author's (or authors') name(s) may + <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> @@ -110,23 +110,23 @@ <sect1> <title>Good-practice recommendations</title> - <para>In addition to the requirements of this license, it is + <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 + <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>All substantive modifications (including + <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>Finally, while it is not mandatory under this + <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> @@ -136,7 +136,7 @@ <sect1 id="sec.opl.options"> <title>License options</title> - <para>The author(s) and/or publisher of an Open + <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 @@ -144,25 +144,25 @@ reference) in derived works.</para> <orderedlist> - <listitem><para>To prohibit distribution of substantively + <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> To accomplish this, add the phrase + <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>To prohibit any publication of this work or + <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>To accomplish this, add the phrase + <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>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/autoid.py Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# +# Add unique ID attributes to para tags. This script should only be +# run by one person, since otherwise it introduces the possibility of +# chaotic conflicts among tags. + +import glob, os, re, sys + +tagged = re.compile('<para[^>]* id="x_([0-9a-f]+)"[^>]*>', re.M) +untagged = re.compile('<para>') + +names = glob.glob('ch*.xml') + glob.glob('app*.xml') + +# First pass: find the highest-numbered paragraph ID. + +biggest_id = 0 +seen = set() +errs = 0 + +for name in names: + for m in tagged.finditer(open(name).read()): + i = int(m.group(1),16) + if i in seen: + print >> sys.stderr, '%s: duplication of ID %s' % (name, i) + errs += 1 + seen.add(i) + if i > biggest_id: + biggest_id = i + +def retag(s): + global biggest_id + biggest_id += 1 + return '<para id="x_%x">' % biggest_id + +# Second pass: add IDs to paragraphs that currently lack them. + +for name in names: + f = open(name).read() + f1 = untagged.sub(retag, f) + if f1 != f: + tmpname = name + '.tmp' + fp = open(tmpname, 'w') + fp.write(f1) + fp.close() + os.rename(tmpname, name) + +sys.exit(errs)
--- a/en/ch00-preface.xml Fri Mar 20 15:40:06 2009 +0800 +++ b/en/ch00-preface.xml Fri Mar 20 16:43:35 2009 +0800 @@ -1,45 +1,162 @@ <!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> <preface id="chap.preface"> + <?dbhtml filename="preface.html"?> <title>Preface</title> - <para>Distributed revision control is a relatively new territory, - and has thus far grown due to people's willingness to strike out - into ill-charted territory.</para> + <sect1> + <title>Why revision control? Why Mercurial?</title> + + <para id="x_6d">Revision control is the process of managing multiple + versions of a piece of information. In its simplest form, this + is something that many people do by hand: every time you modify + a file, save it under a new name that contains a number, each + one higher than the number of the preceding version.</para> + + <para id="x_6e">Manually managing multiple versions of even a single file is + an error-prone task, though, so software tools to help automate + this process have long been available. The earliest automated + revision control tools were intended to help a single user to + manage revisions of a single file. Over the past few decades, + the scope of revision control tools has expanded greatly; they + now manage multiple files, and help multiple people to work + together. The best modern revision control tools have no + problem coping with thousands of people working together on + projects that consist of hundreds of thousands of files.</para> + + <para id="x_6f">The arrival of distributed revision control is relatively + recent, and so far this new field has grown due to people's + willingness to explore ill-charted territory.</para> + + <para id="x_70">I am writing a book about distributed revision control + because I believe that it is an important subject that deserves + a field guide. I chose to write about Mercurial because it is + the easiest tool to learn the terrain with, and yet it scales to + the demands of real, challenging environments where many other + revision control tools buckle.</para> + + <sect2> + <title>Why use revision control?</title> + + <para id="x_71">There are a number of reasons why you or your team might + want to use an automated revision control tool for a + project.</para> - <para>I am writing a book about distributed revision control because - I believe that it is an important subject that deserves a field - guide. I chose to write about Mercurial because it is the easiest - tool to learn the terrain with, and yet it scales to the demands - of real, challenging environments where many other revision - control tools fail.</para> + <itemizedlist> + <listitem><para id="x_72">It will track the history and evolution of + your project, so you don't have to. For every change, + you'll have a log of <emphasis>who</emphasis> made it; + <emphasis>why</emphasis> they made it; + <emphasis>when</emphasis> they made it; and + <emphasis>what</emphasis> the change + was.</para></listitem> + <listitem><para id="x_73">When you're working with other people, + revision control software makes it easier for you to + collaborate. For example, when people more or less + simultaneously make potentially incompatible changes, the + software will help you to identify and resolve those + conflicts.</para></listitem> + <listitem><para id="x_74">It can help you to recover from mistakes. If + you make a change that later turns out to be in error, you + can revert to an earlier version of one or more files. In + fact, a <emphasis>really</emphasis> good revision control + tool will even help you to efficiently figure out exactly + when a problem was introduced (see section <xref + linkend="sec.undo.bisect"/> for details).</para></listitem> + <listitem><para id="x_75">It will help you to work simultaneously on, + and manage the drift between, multiple versions of your + project.</para></listitem> + </itemizedlist> + + <para id="x_76">Most of these reasons are equally valid---at least in + theory---whether you're working on a project by yourself, or + with a hundred other people.</para> + + <para id="x_77">A key question about the practicality of revision control + at these two different scales (<quote>lone hacker</quote> and + <quote>huge team</quote>) is how its + <emphasis>benefits</emphasis> compare to its + <emphasis>costs</emphasis>. A revision control tool that's + difficult to understand or use is going to impose a high + cost.</para> + + <para id="x_78">A five-hundred-person project is likely to collapse under + its own weight almost immediately without a revision control + tool and process. In this case, the cost of using revision + control might hardly seem worth considering, since + <emphasis>without</emphasis> it, failure is almost + guaranteed.</para> + + <para id="x_79">On the other hand, a one-person <quote>quick hack</quote> + might seem like a poor place to use a revision control tool, + because surely the cost of using one must be close to the + overall cost of the project. Right?</para> + + <para id="x_7a">Mercurial uniquely supports <emphasis>both</emphasis> of + these scales of development. You can learn the basics in just + a few minutes, and due to its low overhead, you can apply + revision control to the smallest of projects with ease. Its + simplicity means you won't have a lot of abstruse concepts or + command sequences competing for mental space with whatever + you're <emphasis>really</emphasis> trying to do. At the same + time, Mercurial's high performance and peer-to-peer nature let + you scale painlessly to handle large projects.</para> + + <para id="x_7b">No revision control tool can rescue a poorly run project, + but a good choice of tools can make a huge difference to the + fluidity with which you can work on a project.</para> + + </sect2> + + <sect2> + <title>The many names of revision control</title> + + <para id="x_7c">Revision control is a diverse field, so much so that it is + referred to by many names and acronyms. Here are a few of the + more common variations you'll encounter:</para> + <itemizedlist> + <listitem><para id="x_7d">Revision control (RCS)</para></listitem> + <listitem><para id="x_7e">Software configuration management (SCM), or + configuration management</para></listitem> + <listitem><para id="x_7f">Source code management</para></listitem> + <listitem><para id="x_80">Source code control, or source + control</para></listitem> + <listitem><para id="x_81">Version control + (VCS)</para></listitem></itemizedlist> + <para id="x_82">Some people claim that these terms actually have different + meanings, but in practice they overlap so much that there's no + agreed or even useful way to tease them apart.</para> + + </sect2> + </sect1> <sect1> <title>This book is a work in progress</title> - <para>I am releasing this book while I am still writing it, in the - hope that it will prove useful to others. I also hope that - readers will contribute as they see fit.</para> + <para id="x_83">I am releasing this book while I am still writing it, in the + hope that it will prove useful to others. I am writing under an + open license in the hope that you, my readers, will contribute + feedback and perhaps content of your own.</para> </sect1> <sect1> <title>About the examples in this book</title> - <para>This book takes an unusual approach to code samples. Every + <para id="x_84">This book takes an unusual approach to code samples. Every example is <quote>live</quote>---each one is actually the result of a shell script that executes the Mercurial commands you see. Every time an image of the book is built from its sources, all the example scripts are automatically run, and their current results compared against their expected results.</para> - <para>The advantage of this approach is that the examples are + <para id="x_85">The advantage of this approach is that the examples are always accurate; they describe <emphasis>exactly</emphasis> the behaviour of the version of Mercurial that's mentioned at the front of the book. If I update the version of Mercurial that I'm documenting, and the output of some command changes, the build fails.</para> - <para>There is a small disadvantage to this approach, which is + <para id="x_86">There is a small disadvantage to this approach, which is that the dates and times you'll see in examples tend to be <quote>squashed</quote> together in a way that they wouldn't be if the same commands were being typed by a human. Where a human @@ -47,27 +164,586 @@ resulting timestamps correspondingly spread out, my automated example scripts run many commands in one second.</para> - <para>As an instance of this, several consecutive commits in an + <para id="x_87">As an instance of this, several consecutive commits in an example can show up as having occurred during the same second. You can see this occur in the <literal role="hg-ext">bisect</literal> example in section <xref id="sec.undo.bisect"/>, for instance.</para> - <para>So when you're reading examples, don't place too much weight + <para id="x_88">So when you're reading examples, don't place too much weight on the dates or times you see in the output of commands. But <emphasis>do</emphasis> be confident that the behaviour you're seeing is consistent and reproducible.</para> </sect1> + <sect1> - <title>Colophon---this book is Free</title> + <title>Trends in the field</title> + + <para id="x_89">There has been an unmistakable trend in the development and + use of revision control tools over the past four decades, as + people have become familiar with the capabilities of their tools + and constrained by their limitations.</para> + + <para id="x_8a">The first generation began by managing single files on + individual computers. Although these tools represented a huge + advance over ad-hoc manual revision control, their locking model + and reliance on a single computer limited them to small, + tightly-knit teams.</para> + + <para id="x_8b">The second generation loosened these constraints by moving + to network-centered architectures, and managing entire projects + at a time. As projects grew larger, they ran into new problems. + With clients needing to talk to servers very frequently, server + scaling became an issue for large projects. An unreliable + network connection could prevent remote users from being able to + talk to the server at all. As open source projects started + making read-only access available anonymously to anyone, people + without commit privileges found that they could not use the + tools to interact with a project in a natural way, as they could + not record their changes.</para> + + <para id="x_8c">The current generation of revision control tools is + peer-to-peer in nature. All of these systems have dropped the + dependency on a single central server, and allow people to + distribute their revision control data to where it's actually + needed. Collaboration over the Internet has moved from + constrained by technology to a matter of choice and consensus. + Modern tools can operate offline indefinitely and autonomously, + with a network connection only needed when syncing changes with + another repository.</para> + + </sect1> + <sect1> + <title>A few of the advantages of distributed revision + control</title> + + <para id="x_8d">Even though distributed revision control tools have for + several years been as robust and usable as their + previous-generation counterparts, people using older tools have + not yet necessarily woken up to their advantages. There are a + number of ways in which distributed tools shine relative to + centralised ones.</para> + + <para id="x_8e">For an individual developer, distributed tools are almost + always much faster than centralised tools. This is for a simple + reason: a centralised tool needs to talk over the network for + many common operations, because most metadata is stored in a + single copy on the central server. A distributed tool stores + all of its metadata locally. All else being equal, talking over + the network adds overhead to a centralised tool. Don't + underestimate the value of a snappy, responsive tool: you're + going to spend a lot of time interacting with your revision + control software.</para> + + <para id="x_8f">Distributed tools are indifferent to the vagaries of your + server infrastructure, again because they replicate metadata to + so many locations. If you use a centralised system and your + server catches fire, you'd better hope that your backup media + are reliable, and that your last backup was recent and actually + worked. With a distributed tool, you have many backups + available on every contributor's computer.</para> + + <para id="x_90">The reliability of your network will affect distributed + tools far less than it will centralised tools. You can't even + use a centralised tool without a network connection, except for + a few highly constrained commands. With a distributed tool, if + your network connection goes down while you're working, you may + not even notice. The only thing you won't be able to do is talk + to repositories on other computers, something that is relatively + rare compared with local operations. If you have a far-flung + team of collaborators, this may be significant.</para> + + <sect2> + <title>Advantages for open source projects</title> + + <para id="x_91">If you take a shine to an open source project and decide + that you would like to start hacking on it, and that project + uses a distributed revision control tool, you are at once a + peer with the people who consider themselves the + <quote>core</quote> of that project. If they publish their + repositories, you can immediately copy their project history, + start making changes, and record your work, using the same + tools in the same ways as insiders. By contrast, with a + centralised tool, you must use the software in a <quote>read + only</quote> mode unless someone grants you permission to + commit changes to their central server. Until then, you won't + be able to record changes, and your local modifications will + be at risk of corruption any time you try to update your + client's view of the repository.</para> + + <sect3> + <title>The forking non-problem</title> + + <para id="x_92">It has been suggested that distributed revision control + tools pose some sort of risk to open source projects because + they make it easy to <quote>fork</quote> the development of + a project. A fork happens when there are differences in + opinion or attitude between groups of developers that cause + them to decide that they can't work together any longer. + Each side takes a more or less complete copy of the + project's source code, and goes off in its own + direction.</para> + + <para id="x_93">Sometimes the camps in a fork decide to reconcile their + differences. With a centralised revision control system, the + <emphasis>technical</emphasis> process of reconciliation is + painful, and has to be performed largely by hand. You have + to decide whose revision history is going to + <quote>win</quote>, and graft the other team's changes into + the tree somehow. This usually loses some or all of one + side's revision history.</para> + + <para id="x_94">What distributed tools do with respect to forking is + they make forking the <emphasis>only</emphasis> way to + develop a project. Every single change that you make is + potentially a fork point. The great strength of this + approach is that a distributed revision control tool has to + be really good at <emphasis>merging</emphasis> forks, + because forks are absolutely fundamental: they happen all + the time.</para> + + <para id="x_95">If every piece of work that everybody does, all the + time, is framed in terms of forking and merging, then what + the open source world refers to as a <quote>fork</quote> + becomes <emphasis>purely</emphasis> a social issue. If + anything, distributed tools <emphasis>lower</emphasis> the + likelihood of a fork:</para> + <itemizedlist> + <listitem><para id="x_96">They eliminate the social distinction that + centralised tools impose: that between insiders (people + with commit access) and outsiders (people + without).</para></listitem> + <listitem><para id="x_97">They make it easier to reconcile after a + social fork, because all that's involved from the + perspective of the revision control software is just + another merge.</para></listitem></itemizedlist> + + <para id="x_98">Some people resist distributed tools because they want + to retain tight control over their projects, and they + believe that centralised tools give them this control. + However, if you're of this belief, and you publish your CVS + or Subversion repositories publicly, there are plenty of + tools available that can pull out your entire project's + history (albeit slowly) and recreate it somewhere that you + don't control. So while your control in this case is + illusory, you are forgoing the ability to fluidly + collaborate with whatever people feel compelled to mirror + and fork your history.</para> + + </sect3> + </sect2> + <sect2> + <title>Advantages for commercial projects</title> + + <para id="x_99">Many commercial projects are undertaken by teams that are + scattered across the globe. Contributors who are far from a + central server will see slower command execution and perhaps + less reliability. Commercial revision control systems attempt + to ameliorate these problems with remote-site replication + add-ons that are typically expensive to buy and cantankerous + to administer. A distributed system doesn't suffer from these + problems in the first place. Better yet, you can easily set + up multiple authoritative servers, say one per site, so that + there's no redundant communication between repositories over + expensive long-haul network links.</para> + + <para id="x_9a">Centralised revision control systems tend to have + relatively low scalability. It's not unusual for an expensive + centralised system to fall over under the combined load of + just a few dozen concurrent users. Once again, the typical + response tends to be an expensive and clunky replication + facility. Since the load on a central server---if you have + one at all---is many times lower with a distributed tool + (because all of the data is replicated everywhere), a single + cheap server can handle the needs of a much larger team, and + replication to balance load becomes a simple matter of + scripting.</para> + + <para id="x_9b">If you have an employee in the field, troubleshooting a + problem at a customer's site, they'll benefit from distributed + revision control. The tool will let them generate custom + builds, try different fixes in isolation from each other, and + search efficiently through history for the sources of bugs and + regressions in the customer's environment, all without needing + to connect to your company's network.</para> + + </sect2> + </sect1> + <sect1> + <title>Why choose Mercurial?</title> + + <para id="x_9c">Mercurial has a unique set of properties that make it a + particularly good choice as a revision control system.</para> + <itemizedlist> + <listitem><para id="x_9d">It is easy to learn and use.</para></listitem> + <listitem><para id="x_9e">It is lightweight.</para></listitem> + <listitem><para id="x_9f">It scales excellently.</para></listitem> + <listitem><para id="x_a0">It is easy to + customise.</para></listitem></itemizedlist> + + <para id="x_a1">If you are at all familiar with revision control systems, + you should be able to get up and running with Mercurial in less + than five minutes. Even if not, it will take no more than a few + minutes longer. Mercurial's command and feature sets are + generally uniform and consistent, so you can keep track of a few + general rules instead of a host of exceptions.</para> + + <para id="x_a2">On a small project, you can start working with Mercurial in + moments. Creating new changes and branches; transferring changes + around (whether locally or over a network); and history and + status operations are all fast. Mercurial attempts to stay + nimble and largely out of your way by combining low cognitive + overhead with blazingly fast operations.</para> + + <para id="x_a3">The usefulness of Mercurial is not limited to small + projects: it is used by projects with hundreds to thousands of + contributors, each containing tens of thousands of files and + hundreds of megabytes of source code.</para> + + <para id="x_a4">If the core functionality of Mercurial is not enough for + you, it's easy to build on. Mercurial is well suited to + scripting tasks, and its clean internals and implementation in + Python make it easy to add features in the form of extensions. + There are a number of popular and useful extensions already + available, ranging from helping to identify bugs to improving + performance.</para> + + </sect1> + <sect1> + <title>Mercurial compared with other tools</title> + + <para id="x_a5">Before you read on, please understand that this section + necessarily reflects my own experiences, interests, and (dare I + say it) biases. I have used every one of the revision control + tools listed below, in most cases for several years at a + time.</para> + + + <sect2> + <title>Subversion</title> + + <para id="x_a6">Subversion is a popular revision control tool, developed + to replace CVS. It has a centralised client/server + architecture.</para> + + <para id="x_a7">Subversion and Mercurial have similarly named commands for + performing the same operations, so if you're familiar with + one, it is easy to learn to use the other. Both tools are + portable to all popular operating systems.</para> + + <para id="x_a8">Prior to version 1.5, Subversion had no useful support for + merges. At the time of writing, its merge tracking capability + is new, and known to be <ulink + url="http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced.html#svn.branchmerge.advanced.finalword">complicated + and buggy</ulink>.</para> + + <para id="x_a9">Mercurial has a substantial performance advantage over + Subversion on every revision control operation I have + benchmarked. I have measured its advantage as ranging from a + factor of two to a factor of six when compared with Subversion + 1.4.3's <emphasis>ra_local</emphasis> file store, which is the + fastest access method available. In more realistic + deployments involving a network-based store, Subversion will + be at a substantially larger disadvantage. Because many + Subversion commands must talk to the server and Subversion + does not have useful replication facilities, server capacity + and network bandwidth become bottlenecks for modestly large + projects.</para> - <para>This book is licensed under the Open Publication License, + <para id="x_aa">Additionally, Subversion incurs substantial storage + overhead to avoid network transactions for a few common + operations, such as finding modified files + (<literal>status</literal>) and displaying modifications + against the current revision (<literal>diff</literal>). As a + result, a Subversion working copy is often the same size as, + or larger than, a Mercurial repository and working directory, + even though the Mercurial repository contains a complete + history of the project.</para> + + <para id="x_ab">Subversion is widely supported by third party tools. + Mercurial currently lags considerably in this area. This gap + is closing, however, and indeed some of Mercurial's GUI tools + now outshine their Subversion equivalents. Like Mercurial, + Subversion has an excellent user manual.</para> + + <para id="x_ac">Because Subversion doesn't store revision history on the + client, it is well suited to managing projects that deal with + lots of large, opaque binary files. If you check in fifty + revisions to an incompressible 10MB file, Subversion's + client-side space usage stays constant The space used by any + distributed SCM will grow rapidly in proportion to the number + of revisions, because the differences between each revision + are large.</para> + + <para id="x_ad">In addition, it's often difficult or, more usually, + impossible to merge different versions of a binary file. + Subversion's ability to let a user lock a file, so that they + temporarily have the exclusive right to commit changes to it, + can be a significant advantage to a project where binary files + are widely used.</para> + + <para id="x_ae">Mercurial can import revision history from a Subversion + repository. It can also export revision history to a + Subversion repository. This makes it easy to <quote>test the + waters</quote> and use Mercurial and Subversion in parallel + before deciding to switch. History conversion is incremental, + so you can perform an initial conversion, then small + additional conversions afterwards to bring in new + changes.</para> + + + </sect2> + <sect2> + <title>Git</title> + + <para id="x_af">Git is a distributed revision control tool that was + developed for managing the Linux kernel source tree. Like + Mercurial, its early design was somewhat influenced by + Monotone.</para> + + <para id="x_b0">Git has a very large command set, with version 1.5.0 + providing 139 individual commands. It has something of a + reputation for being difficult to learn. Compared to Git, + Mercurial has a strong focus on simplicity.</para> + + <para id="x_b1">In terms of performance, Git is extremely fast. In + several cases, it is faster than Mercurial, at least on Linux, + while Mercurial performs better on other operations. However, + on Windows, the performance and general level of support that + Git provides is, at the time of writing, far behind that of + Mercurial.</para> + + <para id="x_b2">While a Mercurial repository needs no maintenance, a Git + repository requires frequent manual <quote>repacks</quote> of + its metadata. Without these, performance degrades, while + space usage grows rapidly. A server that contains many Git + repositories that are not rigorously and frequently repacked + will become heavily disk-bound during backups, and there have + been instances of daily backups taking far longer than 24 + hours as a result. A freshly packed Git repository is + slightly smaller than a Mercurial repository, but an unpacked + repository is several orders of magnitude larger.</para> + + <para id="x_b3">The core of Git is written in C. Many Git commands are + implemented as shell or Perl scripts, and the quality of these + scripts varies widely. I have encountered several instances + where scripts charged along blindly in the presence of errors + that should have been fatal.</para> + + <para id="x_b4">Mercurial can import revision history from a Git + repository.</para> + + + </sect2> + <sect2> + <title>CVS</title> + + <para id="x_b5">CVS is probably the most widely used revision control tool + in the world. Due to its age and internal untidiness, it has + been only lightly maintained for many years.</para> + + <para id="x_b6">It has a centralised client/server architecture. It does + not group related file changes into atomic commits, making it + easy for people to <quote>break the build</quote>: one person + can successfully commit part of a change and then be blocked + by the need for a merge, causing other people to see only a + portion of the work they intended to do. This also affects + how you work with project history. If you want to see all of + the modifications someone made as part of a task, you will + need to manually inspect the descriptions and timestamps of + the changes made to each file involved (if you even know what + those files were).</para> + + <para id="x_b7">CVS has a muddled notion of tags and branches that I will + not attempt to even describe. It does not support renaming of + files or directories well, making it easy to corrupt a + repository. It has almost no internal consistency checking + capabilities, so it is usually not even possible to tell + whether or how a repository is corrupt. I would not recommend + CVS for any project, existing or new.</para> + + <para id="x_b8">Mercurial can import CVS revision history. However, there + are a few caveats that apply; these are true of every other + revision control tool's CVS importer, too. Due to CVS's lack + of atomic changes and unversioned filesystem hierarchy, it is + not possible to reconstruct CVS history completely accurately; + some guesswork is involved, and renames will usually not show + up. Because a lot of advanced CVS administration has to be + done by hand and is hence error-prone, it's common for CVS + importers to run into multiple problems with corrupted + repositories (completely bogus revision timestamps and files + that have remained locked for over a decade are just two of + the less interesting problems I can recall from personal + experience).</para> + + <para id="x_b9">Mercurial can import revision history from a CVS + repository.</para> + + + </sect2> + <sect2> + <title>Commercial tools</title> + + <para id="x_ba">Perforce has a centralised client/server architecture, + with no client-side caching of any data. Unlike modern + revision control tools, Perforce requires that a user run a + command to inform the server about every file they intend to + edit.</para> + + <para id="x_bb">The performance of Perforce is quite good for small teams, + but it falls off rapidly as the number of users grows beyond a + few dozen. Modestly large Perforce installations require the + deployment of proxies to cope with the load their users + generate.</para> + + + </sect2> + <sect2> + <title>Choosing a revision control tool</title> + + <para id="x_bc">With the exception of CVS, all of the tools listed above + have unique strengths that suit them to particular styles of + work. There is no single revision control tool that is best + in all situations.</para> + + <para id="x_bd">As an example, Subversion is a good choice for working + with frequently edited binary files, due to its centralised + nature and support for file locking.</para> + + <para id="x_be">I personally find Mercurial's properties of simplicity, + performance, and good merge support to be a compelling + combination that has served me well for several years.</para> + + + </sect2> + </sect1> + <sect1> + <title>Switching from another tool to Mercurial</title> + + <para id="x_bf">Mercurial is bundled with an extension named <literal + role="hg-ext">convert</literal>, which can incrementally + import revision history from several other revision control + tools. By <quote>incremental</quote>, I mean that you can + convert all of a project's history to date in one go, then rerun + the conversion later to obtain new changes that happened after + the initial conversion.</para> + + <para id="x_c0">The revision control tools supported by <literal + role="hg-ext">convert</literal> are as follows:</para> + <itemizedlist> + <listitem><para id="x_c1">Subversion</para></listitem> + <listitem><para id="x_c2">CVS</para></listitem> + <listitem><para id="x_c3">Git</para></listitem> + <listitem><para id="x_c4">Darcs</para></listitem></itemizedlist> + + <para id="x_c5">In addition, <literal role="hg-ext">convert</literal> can + export changes from Mercurial to Subversion. This makes it + possible to try Subversion and Mercurial in parallel before + committing to a switchover, without risking the loss of any + work.</para> + + <para id="x_c6">The <command role="hg-ext-convert">convert</command> command + is easy to use. Simply point it at the path or URL of the + source repository, optionally give it the name of the + destination repository, and it will start working. After the + initial conversion, just run the same command again to import + new changes.</para> + </sect1> + + <sect1> + <title>A short history of revision control</title> + + <para id="x_c7">The best known of the old-time revision control tools is + SCCS (Source Code Control System), which Marc Rochkind wrote at + Bell Labs, in the early 1970s. SCCS operated on individual + files, and required every person working on a project to have + access to a shared workspace on a single system. Only one + person could modify a file at any time; arbitration for access + to files was via locks. It was common for people to lock files, + and later forget to unlock them, preventing anyone else from + modifying those files without the help of an + administrator.</para> + + <para id="x_c8">Walter Tichy developed a free alternative to SCCS in the + early 1980s; he called his program RCS (Revision Control System). + Like SCCS, RCS required developers to work in a single shared + workspace, and to lock files to prevent multiple people from + modifying them simultaneously.</para> + + <para id="x_c9">Later in the 1980s, Dick Grune used RCS as a building block + for a set of shell scripts he initially called cmt, but then + renamed to CVS (Concurrent Versions System). The big innovation + of CVS was that it let developers work simultaneously and + somewhat independently in their own personal workspaces. The + personal workspaces prevented developers from stepping on each + other's toes all the time, as was common with SCCS and RCS. Each + developer had a copy of every project file, and could modify + their copies independently. They had to merge their edits prior + to committing changes to the central repository.</para> + + <para id="x_ca">Brian Berliner took Grune's original scripts and rewrote + them in C, releasing in 1989 the code that has since developed + into the modern version of CVS. CVS subsequently acquired the + ability to operate over a network connection, giving it a + client/server architecture. CVS's architecture is centralised; + only the server has a copy of the history of the project. Client + workspaces just contain copies of recent versions of the + project's files, and a little metadata to tell them where the + server is. CVS has been enormously successful; it is probably + the world's most widely used revision control system.</para> + + <para id="x_cb">In the early 1990s, Sun Microsystems developed an early + distributed revision control system, called TeamWare. A + TeamWare workspace contains a complete copy of the project's + history. TeamWare has no notion of a central repository. (CVS + relied upon RCS for its history storage; TeamWare used + SCCS.)</para> + + <para id="x_cc">As the 1990s progressed, awareness grew of a number of + problems with CVS. It records simultaneous changes to multiple + files individually, instead of grouping them together as a + single logically atomic operation. It does not manage its file + hierarchy well; it is easy to make a mess of a repository by + renaming files and directories. Worse, its source code is + difficult to read and maintain, which made the <quote>pain + level</quote> of fixing these architectural problems + prohibitive.</para> + + <para id="x_cd">In 2001, Jim Blandy and Karl Fogel, two developers who had + worked on CVS, started a project to replace it with a tool that + would have a better architecture and cleaner code. The result, + Subversion, does not stray from CVS's centralised client/server + model, but it adds multi-file atomic commits, better namespace + management, and a number of other features that make it a + generally better tool than CVS. Since its initial release, it + has rapidly grown in popularity.</para> + + <para id="x_ce">More or less simultaneously, Graydon Hoare began working on + an ambitious distributed revision control system that he named + Monotone. While Monotone addresses many of CVS's design flaws + and has a peer-to-peer architecture, it goes beyond earlier (and + subsequent) revision control tools in a number of innovative + ways. It uses cryptographic hashes as identifiers, and has an + integral notion of <quote>trust</quote> for code from different + sources.</para> + + <para id="x_cf">Mercurial began life in 2005. While a few aspects of its + design are influenced by Monotone, Mercurial focuses on ease of + use, high performance, and scalability to very large + projects.</para> + + </sect1> + + <sect1> + <title>Colophon&emdash;this book is Free</title> + + <para id="x_d0">This book is licensed under the Open Publication License, and is produced entirely using Free Software tools. It is typeset with DocBook XML. Illustrations are drawn and rendered with <ulink url="http://www.inkscape.org/">Inkscape</ulink>.</para> - <para>The complete source code for this book is published as a + <para id="x_d1">The complete source code for this book is published as a Mercurial repository, at <ulink url="http://hg.serpentine.com/mercurial/book">http://hg.serpentine.com/mercurial/book</ulink>.</para>
--- a/en/ch01-intro.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,680 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.intro"> - <?dbhtml filename="introduction.html"?> - <title>Introduction</title> - - <sect1> - <title>About revision control</title> - - <para>Revision control is the process of managing multiple - versions of a piece of information. In its simplest form, this - is something that many people do by hand: every time you modify - a file, save it under a new name that contains a number, each - one higher than the number of the preceding version.</para> - - <para>Manually managing multiple versions of even a single file is - an error-prone task, though, so software tools to help automate - this process have long been available. The earliest automated - revision control tools were intended to help a single user to - manage revisions of a single file. Over the past few decades, - the scope of revision control tools has expanded greatly; they - now manage multiple files, and help multiple people to work - together. The best modern revision control tools have no - problem coping with thousands of people working together on - projects that consist of hundreds of thousands of files.</para> - - <sect2> - <title>Why use revision control?</title> - - <para>There are a number of reasons why you or your team might - want to use an automated revision control tool for a - project.</para> - <itemizedlist> - <listitem><para>It will track the history and evolution of - your project, so you don't have to. For every change, - you'll have a log of <emphasis>who</emphasis> made it; - <emphasis>why</emphasis> they made it; - <emphasis>when</emphasis> they made it; and - <emphasis>what</emphasis> the change - was.</para></listitem> - <listitem><para>When you're working with other people, - revision control software makes it easier for you to - collaborate. For example, when people more or less - simultaneously make potentially incompatible changes, the - software will help you to identify and resolve those - conflicts.</para></listitem> - <listitem><para>It can help you to recover from mistakes. If - you make a change that later turns out to be in error, you - can revert to an earlier version of one or more files. In - fact, a <emphasis>really</emphasis> good revision control - tool will even help you to efficiently figure out exactly - when a problem was introduced (see section <xref - linkend="sec.undo.bisect"/> for details).</para></listitem> - <listitem><para>It will help you to work simultaneously on, - and manage the drift between, multiple versions of your - project.</para></listitem></itemizedlist> - <para>Most of these reasons are equally valid---at least in - theory---whether you're working on a project by yourself, or - with a hundred other people.</para> - - <para>A key question about the practicality of revision control - at these two different scales (<quote>lone hacker</quote> and - <quote>huge team</quote>) is how its - <emphasis>benefits</emphasis> compare to its - <emphasis>costs</emphasis>. A revision control tool that's - difficult to understand or use is going to impose a high - cost.</para> - - <para>A five-hundred-person project is likely to collapse under - its own weight almost immediately without a revision control - tool and process. In this case, the cost of using revision - control might hardly seem worth considering, since - <emphasis>without</emphasis> it, failure is almost - guaranteed.</para> - - <para>On the other hand, a one-person <quote>quick hack</quote> - might seem like a poor place to use a revision control tool, - because surely the cost of using one must be close to the - overall cost of the project. Right?</para> - - <para>Mercurial uniquely supports <emphasis>both</emphasis> of - these scales of development. You can learn the basics in just - a few minutes, and due to its low overhead, you can apply - revision control to the smallest of projects with ease. Its - simplicity means you won't have a lot of abstruse concepts or - command sequences competing for mental space with whatever - you're <emphasis>really</emphasis> trying to do. At the same - time, Mercurial's high performance and peer-to-peer nature let - you scale painlessly to handle large projects.</para> - - <para>No revision control tool can rescue a poorly run project, - but a good choice of tools can make a huge difference to the - fluidity with which you can work on a project.</para> - - </sect2> - <sect2> - <title>The many names of revision control</title> - - <para>Revision control is a diverse field, so much so that it - doesn't actually have a single name or acronym. Here are a - few of the more common names and acronyms you'll - encounter:</para> - <itemizedlist> - <listitem><para>Revision control (RCS)</para></listitem> - <listitem><para>Software configuration management (SCM), or - configuration management</para></listitem> - <listitem><para>Source code management</para></listitem> - <listitem><para>Source code control, or source - control</para></listitem> - <listitem><para>Version control - (VCS)</para></listitem></itemizedlist> - <para>Some people claim that these terms actually have different - meanings, but in practice they overlap so much that there's no - agreed or even useful way to tease them apart.</para> - - </sect2> - </sect1> - <sect1> - <title>A short history of revision control</title> - - <para>The best known of the old-time revision control tools is - SCCS (Source Code Control System), which Marc Rochkind wrote at - Bell Labs, in the early 1970s. SCCS operated on individual - files, and required every person working on a project to have - access to a shared workspace on a single system. Only one - person could modify a file at any time; arbitration for access - to files was via locks. It was common for people to lock files, - and later forget to unlock them, preventing anyone else from - modifying those files without the help of an - administrator.</para> - - <para>Walter Tichy developed a free alternative to SCCS in the - early 1980s; he called his program RCS (Revision Control System). - Like SCCS, RCS required developers to work in a single shared - workspace, and to lock files to prevent multiple people from - modifying them simultaneously.</para> - - <para>Later in the 1980s, Dick Grune used RCS as a building block - for a set of shell scripts he initially called cmt, but then - renamed to CVS (Concurrent Versions System). The big innovation - of CVS was that it let developers work simultaneously and - somewhat independently in their own personal workspaces. The - personal workspaces prevented developers from stepping on each - other's toes all the time, as was common with SCCS and RCS. Each - developer had a copy of every project file, and could modify - their copies independently. They had to merge their edits prior - to committing changes to the central repository.</para> - - <para>Brian Berliner took Grune's original scripts and rewrote - them in C, releasing in 1989 the code that has since developed - into the modern version of CVS. CVS subsequently acquired the - ability to operate over a network connection, giving it a - client/server architecture. CVS's architecture is centralised; - only the server has a copy of the history of the project. Client - workspaces just contain copies of recent versions of the - project's files, and a little metadata to tell them where the - server is. CVS has been enormously successful; it is probably - the world's most widely used revision control system.</para> - - <para>In the early 1990s, Sun Microsystems developed an early - distributed revision control system, called TeamWare. A - TeamWare workspace contains a complete copy of the project's - history. TeamWare has no notion of a central repository. (CVS - relied upon RCS for its history storage; TeamWare used - SCCS.)</para> - - <para>As the 1990s progressed, awareness grew of a number of - problems with CVS. It records simultaneous changes to multiple - files individually, instead of grouping them together as a - single logically atomic operation. It does not manage its file - hierarchy well; it is easy to make a mess of a repository by - renaming files and directories. Worse, its source code is - difficult to read and maintain, which made the <quote>pain - level</quote> of fixing these architectural problems - prohibitive.</para> - - <para>In 2001, Jim Blandy and Karl Fogel, two developers who had - worked on CVS, started a project to replace it with a tool that - would have a better architecture and cleaner code. The result, - Subversion, does not stray from CVS's centralised client/server - model, but it adds multi-file atomic commits, better namespace - management, and a number of other features that make it a - generally better tool than CVS. Since its initial release, it - has rapidly grown in popularity.</para> - - <para>More or less simultaneously, Graydon Hoare began working on - an ambitious distributed revision control system that he named - Monotone. While Monotone addresses many of CVS's design flaws - and has a peer-to-peer architecture, it goes beyond earlier (and - subsequent) revision control tools in a number of innovative - ways. It uses cryptographic hashes as identifiers, and has an - integral notion of <quote>trust</quote> for code from different - sources.</para> - - <para>Mercurial began life in 2005. While a few aspects of its - design are influenced by Monotone, Mercurial focuses on ease of - use, high performance, and scalability to very large - projects.</para> - - </sect1> - <sect1> - <title>Trends in revision control</title> - - <para>There has been an unmistakable trend in the development and - use of revision control tools over the past four decades, as - people have become familiar with the capabilities of their tools - and constrained by their limitations.</para> - - <para>The first generation began by managing single files on - individual computers. Although these tools represented a huge - advance over ad-hoc manual revision control, their locking model - and reliance on a single computer limited them to small, - tightly-knit teams.</para> - - <para>The second generation loosened these constraints by moving - to network-centered architectures, and managing entire projects - at a time. As projects grew larger, they ran into new problems. - With clients needing to talk to servers very frequently, server - scaling became an issue for large projects. An unreliable - network connection could prevent remote users from being able to - talk to the server at all. As open source projects started - making read-only access available anonymously to anyone, people - without commit privileges found that they could not use the - tools to interact with a project in a natural way, as they could - not record their changes.</para> - - <para>The current generation of revision control tools is - peer-to-peer in nature. All of these systems have dropped the - dependency on a single central server, and allow people to - distribute their revision control data to where it's actually - needed. Collaboration over the Internet has moved from - constrained by technology to a matter of choice and consensus. - Modern tools can operate offline indefinitely and autonomously, - with a network connection only needed when syncing changes with - another repository.</para> - - </sect1> - <sect1> - <title>A few of the advantages of distributed revision - control</title> - - <para>Even though distributed revision control tools have for - several years been as robust and usable as their - previous-generation counterparts, people using older tools have - not yet necessarily woken up to their advantages. There are a - number of ways in which distributed tools shine relative to - centralised ones.</para> - - <para>For an individual developer, distributed tools are almost - always much faster than centralised tools. This is for a simple - reason: a centralised tool needs to talk over the network for - many common operations, because most metadata is stored in a - single copy on the central server. A distributed tool stores - all of its metadata locally. All else being equal, talking over - the network adds overhead to a centralised tool. Don't - underestimate the value of a snappy, responsive tool: you're - going to spend a lot of time interacting with your revision - control software.</para> - - <para>Distributed tools are indifferent to the vagaries of your - server infrastructure, again because they replicate metadata to - so many locations. If you use a centralised system and your - server catches fire, you'd better hope that your backup media - are reliable, and that your last backup was recent and actually - worked. With a distributed tool, you have many backups - available on every contributor's computer.</para> - - <para>The reliability of your network will affect distributed - tools far less than it will centralised tools. You can't even - use a centralised tool without a network connection, except for - a few highly constrained commands. With a distributed tool, if - your network connection goes down while you're working, you may - not even notice. The only thing you won't be able to do is talk - to repositories on other computers, something that is relatively - rare compared with local operations. If you have a far-flung - team of collaborators, this may be significant.</para> - - <sect2> - <title>Advantages for open source projects</title> - - <para>If you take a shine to an open source project and decide - that you would like to start hacking on it, and that project - uses a distributed revision control tool, you are at once a - peer with the people who consider themselves the - <quote>core</quote> of that project. If they publish their - repositories, you can immediately copy their project history, - start making changes, and record your work, using the same - tools in the same ways as insiders. By contrast, with a - centralised tool, you must use the software in a <quote>read - only</quote> mode unless someone grants you permission to - commit changes to their central server. Until then, you won't - be able to record changes, and your local modifications will - be at risk of corruption any time you try to update your - client's view of the repository.</para> - - <sect3> - <title>The forking non-problem</title> - - <para>It has been suggested that distributed revision control - tools pose some sort of risk to open source projects because - they make it easy to <quote>fork</quote> the development of - a project. A fork happens when there are differences in - opinion or attitude between groups of developers that cause - them to decide that they can't work together any longer. - Each side takes a more or less complete copy of the - project's source code, and goes off in its own - direction.</para> - - <para>Sometimes the camps in a fork decide to reconcile their - differences. With a centralised revision control system, the - <emphasis>technical</emphasis> process of reconciliation is - painful, and has to be performed largely by hand. You have - to decide whose revision history is going to - <quote>win</quote>, and graft the other team's changes into - the tree somehow. This usually loses some or all of one - side's revision history.</para> - - <para>What distributed tools do with respect to forking is - they make forking the <emphasis>only</emphasis> way to - develop a project. Every single change that you make is - potentially a fork point. The great strength of this - approach is that a distributed revision control tool has to - be really good at <emphasis>merging</emphasis> forks, - because forks are absolutely fundamental: they happen all - the time.</para> - - <para>If every piece of work that everybody does, all the - time, is framed in terms of forking and merging, then what - the open source world refers to as a <quote>fork</quote> - becomes <emphasis>purely</emphasis> a social issue. If - anything, distributed tools <emphasis>lower</emphasis> the - likelihood of a fork:</para> - <itemizedlist> - <listitem><para>They eliminate the social distinction that - centralised tools impose: that between insiders (people - with commit access) and outsiders (people - without).</para></listitem> - <listitem><para>They make it easier to reconcile after a - social fork, because all that's involved from the - perspective of the revision control software is just - another merge.</para></listitem></itemizedlist> - - <para>Some people resist distributed tools because they want - to retain tight control over their projects, and they - believe that centralised tools give them this control. - However, if you're of this belief, and you publish your CVS - or Subversion repositories publicly, there are plenty of - tools available that can pull out your entire project's - history (albeit slowly) and recreate it somewhere that you - don't control. So while your control in this case is - illusory, you are forgoing the ability to fluidly - collaborate with whatever people feel compelled to mirror - and fork your history.</para> - - </sect3> - </sect2> - <sect2> - <title>Advantages for commercial projects</title> - - <para>Many commercial projects are undertaken by teams that are - scattered across the globe. Contributors who are far from a - central server will see slower command execution and perhaps - less reliability. Commercial revision control systems attempt - to ameliorate these problems with remote-site replication - add-ons that are typically expensive to buy and cantankerous - to administer. A distributed system doesn't suffer from these - problems in the first place. Better yet, you can easily set - up multiple authoritative servers, say one per site, so that - there's no redundant communication between repositories over - expensive long-haul network links.</para> - - <para>Centralised revision control systems tend to have - relatively low scalability. It's not unusual for an expensive - centralised system to fall over under the combined load of - just a few dozen concurrent users. Once again, the typical - response tends to be an expensive and clunky replication - facility. Since the load on a central server---if you have - one at all---is many times lower with a distributed tool - (because all of the data is replicated everywhere), a single - cheap server can handle the needs of a much larger team, and - replication to balance load becomes a simple matter of - scripting.</para> - - <para>If you have an employee in the field, troubleshooting a - problem at a customer's site, they'll benefit from distributed - revision control. The tool will let them generate custom - builds, try different fixes in isolation from each other, and - search efficiently through history for the sources of bugs and - regressions in the customer's environment, all without needing - to connect to your company's network.</para> - - </sect2> - </sect1> - <sect1> - <title>Why choose Mercurial?</title> - - <para>Mercurial has a unique set of properties that make it a - particularly good choice as a revision control system.</para> - <itemizedlist> - <listitem><para>It is easy to learn and use.</para></listitem> - <listitem><para>It is lightweight.</para></listitem> - <listitem><para>It scales excellently.</para></listitem> - <listitem><para>It is easy to - customise.</para></listitem></itemizedlist> - - <para>If you are at all familiar with revision control systems, - you should be able to get up and running with Mercurial in less - than five minutes. Even if not, it will take no more than a few - minutes longer. Mercurial's command and feature sets are - generally uniform and consistent, so you can keep track of a few - general rules instead of a host of exceptions.</para> - - <para>On a small project, you can start working with Mercurial in - moments. Creating new changes and branches; transferring changes - around (whether locally or over a network); and history and - status operations are all fast. Mercurial attempts to stay - nimble and largely out of your way by combining low cognitive - overhead with blazingly fast operations.</para> - - <para>The usefulness of Mercurial is not limited to small - projects: it is used by projects with hundreds to thousands of - contributors, each containing tens of thousands of files and - hundreds of megabytes of source code.</para> - - <para>If the core functionality of Mercurial is not enough for - you, it's easy to build on. Mercurial is well suited to - scripting tasks, and its clean internals and implementation in - Python make it easy to add features in the form of extensions. - There are a number of popular and useful extensions already - available, ranging from helping to identify bugs to improving - performance.</para> - - </sect1> - <sect1> - <title>Mercurial compared with other tools</title> - - <para>Before you read on, please understand that this section - necessarily reflects my own experiences, interests, and (dare I - say it) biases. I have used every one of the revision control - tools listed below, in most cases for several years at a - time.</para> - - - <sect2> - <title>Subversion</title> - - <para>Subversion is a popular revision control tool, developed - to replace CVS. It has a centralised client/server - architecture.</para> - - <para>Subversion and Mercurial have similarly named commands for - performing the same operations, so if you're familiar with - one, it is easy to learn to use the other. Both tools are - portable to all popular operating systems.</para> - - <para>Prior to version 1.5, Subversion had no useful support for - merges. At the time of writing, its merge tracking capability - is new, and known to be <ulink - url="http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced.html#svn.branchmerge.advanced.finalword">complicated - and buggy</ulink>.</para> - - <para>Mercurial has a substantial performance advantage over - Subversion on every revision control operation I have - benchmarked. I have measured its advantage as ranging from a - factor of two to a factor of six when compared with Subversion - 1.4.3's <emphasis>ra_local</emphasis> file store, which is the - fastest access method available. In more realistic - deployments involving a network-based store, Subversion will - be at a substantially larger disadvantage. Because many - Subversion commands must talk to the server and Subversion - does not have useful replication facilities, server capacity - and network bandwidth become bottlenecks for modestly large - projects.</para> - - <para>Additionally, Subversion incurs substantial storage - overhead to avoid network transactions for a few common - operations, such as finding modified files - (<literal>status</literal>) and displaying modifications - against the current revision (<literal>diff</literal>). As a - result, a Subversion working copy is often the same size as, - or larger than, a Mercurial repository and working directory, - even though the Mercurial repository contains a complete - history of the project.</para> - - <para>Subversion is widely supported by third party tools. - Mercurial currently lags considerably in this area. This gap - is closing, however, and indeed some of Mercurial's GUI tools - now outshine their Subversion equivalents. Like Mercurial, - Subversion has an excellent user manual.</para> - - <para>Because Subversion doesn't store revision history on the - client, it is well suited to managing projects that deal with - lots of large, opaque binary files. If you check in fifty - revisions to an incompressible 10MB file, Subversion's - client-side space usage stays constant The space used by any - distributed SCM will grow rapidly in proportion to the number - of revisions, because the differences between each revision - are large.</para> - - <para>In addition, it's often difficult or, more usually, - impossible to merge different versions of a binary file. - Subversion's ability to let a user lock a file, so that they - temporarily have the exclusive right to commit changes to it, - can be a significant advantage to a project where binary files - are widely used.</para> - - <para>Mercurial can import revision history from a Subversion - repository. It can also export revision history to a - Subversion repository. This makes it easy to <quote>test the - waters</quote> and use Mercurial and Subversion in parallel - before deciding to switch. History conversion is incremental, - so you can perform an initial conversion, then small - additional conversions afterwards to bring in new - changes.</para> - - - </sect2> - <sect2> - <title>Git</title> - - <para>Git is a distributed revision control tool that was - developed for managing the Linux kernel source tree. Like - Mercurial, its early design was somewhat influenced by - Monotone.</para> - - <para>Git has a very large command set, with version 1.5.0 - providing 139 individual commands. It has something of a - reputation for being difficult to learn. Compared to Git, - Mercurial has a strong focus on simplicity.</para> - - <para>In terms of performance, Git is extremely fast. In - several cases, it is faster than Mercurial, at least on Linux, - while Mercurial performs better on other operations. However, - on Windows, the performance and general level of support that - Git provides is, at the time of writing, far behind that of - Mercurial.</para> - - <para>While a Mercurial repository needs no maintenance, a Git - repository requires frequent manual <quote>repacks</quote> of - its metadata. Without these, performance degrades, while - space usage grows rapidly. A server that contains many Git - repositories that are not rigorously and frequently repacked - will become heavily disk-bound during backups, and there have - been instances of daily backups taking far longer than 24 - hours as a result. A freshly packed Git repository is - slightly smaller than a Mercurial repository, but an unpacked - repository is several orders of magnitude larger.</para> - - <para>The core of Git is written in C. Many Git commands are - implemented as shell or Perl scripts, and the quality of these - scripts varies widely. I have encountered several instances - where scripts charged along blindly in the presence of errors - that should have been fatal.</para> - - <para>Mercurial can import revision history from a Git - repository.</para> - - - </sect2> - <sect2> - <title>CVS</title> - - <para>CVS is probably the most widely used revision control tool - in the world. Due to its age and internal untidiness, it has - been only lightly maintained for many years.</para> - - <para>It has a centralised client/server architecture. It does - not group related file changes into atomic commits, making it - easy for people to <quote>break the build</quote>: one person - can successfully commit part of a change and then be blocked - by the need for a merge, causing other people to see only a - portion of the work they intended to do. This also affects - how you work with project history. If you want to see all of - the modifications someone made as part of a task, you will - need to manually inspect the descriptions and timestamps of - the changes made to each file involved (if you even know what - those files were).</para> - - <para>CVS has a muddled notion of tags and branches that I will - not attempt to even describe. It does not support renaming of - files or directories well, making it easy to corrupt a - repository. It has almost no internal consistency checking - capabilities, so it is usually not even possible to tell - whether or how a repository is corrupt. I would not recommend - CVS for any project, existing or new.</para> - - <para>Mercurial can import CVS revision history. However, there - are a few caveats that apply; these are true of every other - revision control tool's CVS importer, too. Due to CVS's lack - of atomic changes and unversioned filesystem hierarchy, it is - not possible to reconstruct CVS history completely accurately; - some guesswork is involved, and renames will usually not show - up. Because a lot of advanced CVS administration has to be - done by hand and is hence error-prone, it's common for CVS - importers to run into multiple problems with corrupted - repositories (completely bogus revision timestamps and files - that have remained locked for over a decade are just two of - the less interesting problems I can recall from personal - experience).</para> - - <para>Mercurial can import revision history from a CVS - repository.</para> - - - </sect2> - <sect2> - <title>Commercial tools</title> - - <para>Perforce has a centralised client/server architecture, - with no client-side caching of any data. Unlike modern - revision control tools, Perforce requires that a user run a - command to inform the server about every file they intend to - edit.</para> - - <para>The performance of Perforce is quite good for small teams, - but it falls off rapidly as the number of users grows beyond a - few dozen. Modestly large Perforce installations require the - deployment of proxies to cope with the load their users - generate.</para> - - - </sect2> - <sect2> - <title>Choosing a revision control tool</title> - - <para>With the exception of CVS, all of the tools listed above - have unique strengths that suit them to particular styles of - work. There is no single revision control tool that is best - in all situations.</para> - - <para>As an example, Subversion is a good choice for working - with frequently edited binary files, due to its centralised - nature and support for file locking.</para> - - <para>I personally find Mercurial's properties of simplicity, - performance, and good merge support to be a compelling - combination that has served me well for several years.</para> - - - </sect2> - </sect1> - <sect1> - <title>Switching from another tool to Mercurial</title> - - <para>Mercurial is bundled with an extension named <literal - role="hg-ext">convert</literal>, which can incrementally - import revision history from several other revision control - tools. By <quote>incremental</quote>, I mean that you can - convert all of a project's history to date in one go, then rerun - the conversion later to obtain new changes that happened after - the initial conversion.</para> - - <para>The revision control tools supported by <literal - role="hg-ext">convert</literal> are as follows:</para> - <itemizedlist> - <listitem><para>Subversion</para></listitem> - <listitem><para>CVS</para></listitem> - <listitem><para>Git</para></listitem> - <listitem><para>Darcs</para></listitem></itemizedlist> - - <para>In addition, <literal role="hg-ext">convert</literal> can - export changes from Mercurial to Subversion. This makes it - possible to try Subversion and Mercurial in parallel before - committing to a switchover, without risking the loss of any - work.</para> - - <para>The <command role="hg-ext-conver">convert</command> command - is easy to use. Simply point it at the path or URL of the - source repository, optionally give it the name of the - destination repository, and it will start working. After the - initial conversion, just run the same command again to import - new changes.</para> - </sect1> -</chapter> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch01-tour-basic.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,862 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.tour-basic"> + <?dbhtml filename="a-tour-of-mercurial-the-basics.html"?> + <title>A tour of Mercurial: the basics</title> + + <sect1 id="sec.tour.install"> + <title>Installing Mercurial on your system</title> + + <para>Prebuilt binary packages of Mercurial are available for + every popular operating system. These make it easy to start + using Mercurial on your computer immediately.</para> + + <sect2> + <title>Linux</title> + + <para>Because each Linux distribution has its own packaging + tools, policies, and rate of development, it's difficult to + give a comprehensive set of instructions on how to install + Mercurial binaries. The version of Mercurial that you will + end up with can vary depending on how active the person is who + maintains the package for your distribution.</para> + + <para>To keep things simple, I will focus on installing + Mercurial from the command line under the most popular Linux + distributions. Most of these distributions provide graphical + package managers that will let you install Mercurial with a + single click; the package name to look for is + <literal>mercurial</literal>.</para> + + <itemizedlist> + <listitem><para>Debian:</para> + <programlisting>apt-get install mercurial</programlisting></listitem> + <listitem><para>Fedora Core:</para> + <programlisting>yum install mercurial</programlisting></listitem> + <listitem><para>Gentoo:</para> + <programlisting>emerge mercurial</programlisting></listitem> + <listitem><para>OpenSUSE:</para> + <programlisting>yum install mercurial</programlisting></listitem> + <listitem><para>Ubuntu: Ubuntu's Mercurial package is based on + Debian's. To install it, run the following + command.</para> + <programlisting>apt-get install mercurial</programlisting></listitem> + </itemizedlist> + + </sect2> + <sect2> + <title>Solaris</title> + + <para>SunFreeWare, at <ulink + url="http://www.sunfreeware.com">http://www.sunfreeware.com</ulink>, + is a good source for a large number of pre-built Solaris + packages for 32 and 64 bit Intel and Sparc architectures, + including current versions of Mercurial.</para> + + </sect2> + <sect2> + <title>Mac OS X</title> + + <para>Lee Cantey publishes an installer of Mercurial for Mac OS + X at <ulink + url="http://mercurial.berkwood.com">http://mercurial.berkwood.com</ulink>. + This package works on both Intel- and Power-based Macs. Before + you can use it, you must install a compatible version of + Universal MacPython <citation>web:macpython</citation>. This + is easy to do; simply follow the instructions on Lee's + site.</para> + + <para>It's also possible to install Mercurial using Fink or + MacPorts, two popular free package managers for Mac OS X. If + you have Fink, use <command>sudo apt-get install + mercurial-py25</command>. If MacPorts, <command>sudo port + install mercurial</command>.</para> + + </sect2> + <sect2> + <title>Windows</title> + + <para>Lee Cantey publishes an installer of Mercurial for Windows + at <ulink + url="http://mercurial.berkwood.com">http://mercurial.berkwood.com</ulink>. + This package has no external dependencies; it <quote>just + works</quote>.</para> + + <note> + <para> The Windows version of Mercurial does not + automatically convert line endings between Windows and Unix + styles. If you want to share work with Unix users, you must + do a little additional configuration work. XXX Flesh this + out.</para> + </note> + + </sect2> + </sect1> + <sect1> + <title>Getting started</title> + + <para>To begin, we'll use the <command role="hg-cmd">hg + version</command> command to find out whether Mercurial is + actually installed properly. The actual version information + that it prints isn't so important; it's whether it prints + anything at all that we care about.</para> + + &interaction.tour.version; + + <sect2> + <title>Built-in help</title> + + <para>Mercurial provides a built-in help system. This is + invaluable for those times when you find yourself stuck + trying to remember how to run a command. If you are + completely stuck, simply run <command role="hg-cmd">hg + help</command>; it will print a brief list of commands, + along with a description of what each does. If you ask for + help on a specific command (as below), it prints more + detailed information.</para> + + &interaction.tour.help; + + <para>For a more impressive level of detail (which you won't + usually need) run <command role="hg-cmd">hg help <option + role="hg-opt-global">-v</option></command>. The <option + role="hg-opt-global">-v</option> option is short for + <option role="hg-opt-global">--verbose</option>, and tells + Mercurial to print more information than it usually + would.</para> + + </sect2> + </sect1> + <sect1> + <title>Working with a repository</title> + + <para>In Mercurial, everything happens inside a + <emphasis>repository</emphasis>. The repository for a project + contains all of the files that <quote>belong to</quote> that + project, along with a historical record of the project's + files.</para> + + <para>There's nothing particularly magical about a repository; it + is simply a directory tree in your filesystem that Mercurial + treats as special. You can rename or delete a repository any + time you like, using either the command line or your file + browser.</para> + + <sect2> + <title>Making a local copy of a repository</title> + + <para><emphasis>Copying</emphasis> a repository is just a little + bit special. While you could use a normal file copying + command to make a copy of a repository, it's best to use a + built-in command that Mercurial provides. This command is + called <command role="hg-cmd">hg clone</command>, because it + creates an identical copy of an existing repository.</para> + + &interaction.tour.clone; + + <para>If our clone succeeded, we should now have a local + directory called <filename class="directory">hello</filename>. + This directory will contain some files.</para> + + &interaction.tour.ls; + + <para>These files have the same contents and history in our + repository as they do in the repository we cloned.</para> + + <para>Every Mercurial repository is complete, self-contained, + and independent. It contains its own private copy of a + project's files and history. A cloned repository remembers + the location of the repository it was cloned from, but it does + not communicate with that repository, or any other, unless you + tell it to.</para> + + <para>What this means for now is that we're free to experiment + with our repository, safe in the knowledge that it's a private + <quote>sandbox</quote> that won't affect anyone else.</para> + + </sect2> + <sect2> + <title>What's in a repository?</title> + + <para>When we take a more detailed look inside a repository, we + can see that it contains a directory named <filename + class="directory">.hg</filename>. This is where Mercurial + keeps all of its metadata for the repository.</para> + + &interaction.tour.ls-a; + + <para>The contents of the <filename + class="directory">.hg</filename> directory and its + subdirectories are private to Mercurial. Every other file and + directory in the repository is yours to do with as you + please.</para> + + <para>To introduce a little terminology, the <filename + class="directory">.hg</filename> directory is the + <quote>real</quote> repository, and all of the files and + directories that coexist with it are said to live in the + <emphasis>working directory</emphasis>. An easy way to + remember the distinction is that the + <emphasis>repository</emphasis> contains the + <emphasis>history</emphasis> of your project, while the + <emphasis>working directory</emphasis> contains a + <emphasis>snapshot</emphasis> of your project at a particular + point in history.</para> + + </sect2> + </sect1> + <sect1> + <title>A tour through history</title> + + <para>One of the first things we might want to do with a new, + unfamiliar repository is understand its history. The <command + role="hg-cmd">hg log</command> command gives us a view of + history.</para> + + &interaction.tour.log; + + <para>By default, this command prints a brief paragraph of output + for each change to the project that was recorded. In Mercurial + terminology, we call each of these recorded events a + <emphasis>changeset</emphasis>, because it can contain a record + of changes to several files.</para> + + <para>The fields in a record of output from <command + role="hg-cmd">hg log</command> are as follows.</para> + <itemizedlist> + <listitem><para><literal>changeset</literal>: This field has the + format of a number, followed by a colon, followed by a + hexadecimal string. These are + <emphasis>identifiers</emphasis> for the changeset. There + are two identifiers because the number is shorter and easier + to type than the hex string.</para></listitem> + <listitem><para><literal>user</literal>: The identity of the + person who created the changeset. This is a free-form + field, but it most often contains a person's name and email + address.</para></listitem> + <listitem><para><literal>date</literal>: The date and time on + which the changeset was created, and the timezone in which + it was created. (The date and time are local to that + timezone; they display what time and date it was for the + person who created the changeset.)</para></listitem> + <listitem><para><literal>summary</literal>: The first line of + the text message that the creator of the changeset entered + to describe the changeset.</para></listitem></itemizedlist> + <para>The default output printed by <command role="hg-cmd">hg + log</command> is purely a summary; it is missing a lot of + detail.</para> + + <para>Figure <xref endterm="fig.tour-basic.history.caption" + linkend="fig.tour-basic.history"/> provides a + graphical representation of the history of the <filename + class="directory">hello</filename> repository, to make it a + little easier to see which direction history is + <quote>flowing</quote> in. We'll be returning to this figure + several times in this chapter and the chapter that + follows.</para> + + <informalfigure id="fig.tour-basic.history"> + <mediaobject> + <imageobject><imagedata fileref="images/tour-history.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.tour-basic.history.caption">Graphical history of + the <filename class="directory">hello</filename> repository</para> + </caption> + </mediaobject> + </informalfigure> + + <sect2> + <title>Changesets, revisions, and talking to other + people</title> + + <para>As English is a notoriously sloppy language, and computer + science has a hallowed history of terminological confusion + (why use one term when four will do?), revision control has a + variety of words and phrases that mean the same thing. If you + are talking about Mercurial history with other people, you + will find that the word <quote>changeset</quote> is often + compressed to <quote>change</quote> or (when written) + <quote>cset</quote>, and sometimes a changeset is referred to + as a <quote>revision</quote> or a <quote>rev</quote>.</para> + + <para>While it doesn't matter what <emphasis>word</emphasis> you + use to refer to the concept of <quote>a changeset</quote>, the + <emphasis>identifier</emphasis> that you use to refer to + <quote>a <emphasis>specific</emphasis> changeset</quote> is of + great importance. Recall that the <literal>changeset</literal> + field in the output from <command role="hg-cmd">hg + log</command> identifies a changeset using both a number and + a hexadecimal string.</para> + <itemizedlist> + <listitem><para>The revision number is <emphasis>only valid in + that repository</emphasis>,</para></listitem> + <listitem><para>while the hex string is the + <emphasis>permanent, unchanging identifier</emphasis> that + will always identify that exact changeset in + <emphasis>every</emphasis> copy of the + repository.</para></listitem></itemizedlist> + <para>This distinction is important. If you send someone an + email talking about <quote>revision 33</quote>, there's a high + likelihood that their revision 33 will <emphasis>not be the + same</emphasis> as yours. The reason for this is that a + revision number depends on the order in which changes arrived + in a repository, and there is no guarantee that the same + changes will happen in the same order in different + repositories. Three changes $a,b,c$ can easily appear in one + repository as $0,1,2$, while in another as $1,0,2$.</para> + + <para>Mercurial uses revision numbers purely as a convenient + shorthand. If you need to discuss a changeset with someone, + or make a record of a changeset for some other reason (for + example, in a bug report), use the hexadecimal + identifier.</para> + + </sect2> + <sect2> + <title>Viewing specific revisions</title> + + <para>To narrow the output of <command role="hg-cmd">hg + log</command> down to a single revision, use the <option + role="hg-opt-log">-r</option> (or <option + role="hg-opt-log">--rev</option>) option. You can use + either a revision number or a long-form changeset identifier, + and you can provide as many revisions as you want.</para> + + &interaction.tour.log-r; + + <para>If you want to see the history of several revisions + without having to list each one, you can use <emphasis>range + notation</emphasis>; this lets you express the idea <quote>I + want all revisions between <literal>abc</literal> and + <literal>def</literal>, inclusive</quote>.</para> + + &interaction.tour.log.range; + + <para>Mercurial also honours the order in which you specify + revisions, so <command role="hg-cmd">hg log -r 2:4</command> + prints 2, 3, and 4. while <command role="hg-cmd">hg log -r + 4:2</command> prints 4, 3, and 2.</para> + + </sect2> + <sect2> + <title>More detailed information</title> + + <para>While the summary information printed by <command + role="hg-cmd">hg log</command> is useful if you already know + what you're looking for, you may need to see a complete + description of the change, or a list of the files changed, if + you're trying to decide whether a changeset is the one you're + looking for. The <command role="hg-cmd">hg log</command> + command's <option role="hg-opt-global">-v</option> (or <option + role="hg-opt-global">--verbose</option>) option gives you + this extra detail.</para> + + &interaction.tour.log-v; + + <para>If you want to see both the description and content of a + change, add the <option role="hg-opt-log">-p</option> (or + <option role="hg-opt-log">--patch</option>) option. This + displays the content of a change as a <emphasis>unified + diff</emphasis> (if you've never seen a unified diff before, + see section <xref linkend="sec.mq.patch"/> for an + overview).</para> + + &interaction.tour.log-vp; + + </sect2> + </sect1> + <sect1> + <title>All about command options</title> + + <para>Let's take a brief break from exploring Mercurial commands + to discuss a pattern in the way that they work; you may find + this useful to keep in mind as we continue our tour.</para> + + <para>Mercurial has a consistent and straightforward approach to + dealing with the options that you can pass to commands. It + follows the conventions for options that are common to modern + Linux and Unix systems.</para> + <itemizedlist> + <listitem><para>Every option has a long name. For example, as + we've already seen, the <command role="hg-cmd">hg + log</command> command accepts a <option + role="hg-opt-log">--rev</option> option.</para></listitem> + <listitem><para>Most options have short names, too. Instead of + <option role="hg-opt-log">--rev</option>, we can use <option + role="hg-opt-log">-r</option>. (The reason that some + options don't have short names is that the options in + question are rarely used.)</para></listitem> + <listitem><para>Long options start with two dashes (e.g. <option + role="hg-opt-log">--rev</option>), while short options + start with one (e.g. <option + role="hg-opt-log">-r</option>).</para></listitem> + <listitem><para>Option naming and usage is consistent across + commands. For example, every command that lets you specify + a changeset ID or revision number accepts both <option + role="hg-opt-log">-r</option> and <option + role="hg-opt-log">--rev</option> + arguments.</para></listitem></itemizedlist> + <para>In the examples throughout this book, I use short options + instead of long. This just reflects my own preference, so don't + read anything significant into it.</para> + + <para>Most commands that print output of some kind will print more + output when passed a <option role="hg-opt-global">-v</option> + (or <option role="hg-opt-global">--verbose</option>) option, and + less when passed <option role="hg-opt-global">-q</option> (or + <option role="hg-opt-global">--quiet</option>).</para> + + </sect1> + <sect1> + <title>Making and reviewing changes</title> + + <para>Now that we have a grasp of viewing history in Mercurial, + let's take a look at making some changes and examining + them.</para> + + <para>The first thing we'll do is isolate our experiment in a + repository of its own. We use the <command role="hg-cmd">hg + clone</command> command, but we don't need to clone a copy of + the remote repository. Since we already have a copy of it + locally, we can just clone that instead. This is much faster + than cloning over the network, and cloning a local repository + uses less disk space in most cases, too.</para> + + &interaction.tour.reclone; + + <para>As an aside, it's often good practice to keep a + <quote>pristine</quote> copy of a remote repository around, + which you can then make temporary clones of to create sandboxes + for each task you want to work on. This lets you work on + multiple tasks in parallel, each isolated from the others until + it's complete and you're ready to integrate it back. Because + local clones are so cheap, there's almost no overhead to cloning + and destroying repositories whenever you want.</para> + + <para>In our <filename class="directory">my-hello</filename> + repository, we have a file <filename>hello.c</filename> that + contains the classic <quote>hello, world</quote> program. Let's + use the ancient and venerable <command>sed</command> command to + edit this file so that it prints a second line of output. (I'm + only using <command>sed</command> to do this because it's easy + to write a scripted example this way. Since you're not under + the same constraint, you probably won't want to use + <command>sed</command>; simply use your preferred text editor to + do the same thing.)</para> + + &interaction.tour.sed; + + <para>Mercurial's <command role="hg-cmd">hg status</command> + command will tell us what Mercurial knows about the files in the + repository.</para> + + &interaction.tour.status; + + <para>The <command role="hg-cmd">hg status</command> command + prints no output for some files, but a line starting with + <quote><literal>M</literal></quote> for + <filename>hello.c</filename>. Unless you tell it to, <command + role="hg-cmd">hg status</command> will not print any output + for files that have not been modified.</para> + + <para>The <quote><literal>M</literal></quote> indicates that + Mercurial has noticed that we modified + <filename>hello.c</filename>. We didn't need to + <emphasis>inform</emphasis> Mercurial that we were going to + modify the file before we started, or that we had modified the + file after we were done; it was able to figure this out + itself.</para> + + <para>It's a little bit helpful to know that we've modified + <filename>hello.c</filename>, but we might prefer to know + exactly <emphasis>what</emphasis> changes we've made to it. To + do this, we use the <command role="hg-cmd">hg diff</command> + command.</para> + + &interaction.tour.diff; + + </sect1> + <sect1> + <title>Recording changes in a new changeset</title> + + <para>We can modify files, build and test our changes, and use + <command role="hg-cmd">hg status</command> and <command + role="hg-cmd">hg diff</command> to review our changes, until + we're satisfied with what we've done and arrive at a natural + stopping point where we want to record our work in a new + changeset.</para> + + <para>The <command role="hg-cmd">hg commit</command> command lets + us create a new changeset; we'll usually refer to this as + <quote>making a commit</quote> or + <quote>committing</quote>.</para> + + <sect2> + <title>Setting up a username</title> + + <para>When you try to run <command role="hg-cmd">hg + commit</command> for the first time, it is not guaranteed to + succeed. Mercurial records your name and address with each + change that you commit, so that you and others will later be + able to tell who made each change. Mercurial tries to + automatically figure out a sensible username to commit the + change with. It will attempt each of the following methods, + in order:</para> + <orderedlist> + <listitem><para>If you specify a <option + role="hg-opt-commit">-u</option> option to the <command + role="hg-cmd">hg commit</command> command on the command + line, followed by a username, this is always given the + highest precedence.</para></listitem> + <listitem><para>If you have set the <envar>HGUSER</envar> + environment variable, this is checked + next.</para></listitem> + <listitem><para>If you create a file in your home directory + called <filename role="special">.hgrc</filename>, with a + <envar role="rc-item-ui">username</envar> entry, that will + be used next. To see what the contents of this file + should look like, refer to section <xref + linkend="sec.tour-basic.username"/> + below.</para></listitem> + <listitem><para>If you have set the <envar>EMAIL</envar> + environment variable, this will be used + next.</para></listitem> + <listitem><para>Mercurial will query your system to find out + your local user name and host name, and construct a + username from these components. Since this often results + in a username that is not very useful, it will print a + warning if it has to do + this.</para></listitem> + </orderedlist> + <para>If all of these mechanisms fail, Mercurial will + fail, printing an error message. In this case, it will not + let you commit until you set up a + username.</para> + <para>You should think of the <envar>HGUSER</envar> environment + variable and the <option role="hg-opt-commit">-u</option> + option to the <command role="hg-cmd">hg commit</command> + command as ways to <emphasis>override</emphasis> Mercurial's + default selection of username. For normal use, the simplest + and most robust way to set a username for yourself is by + creating a <filename role="special">.hgrc</filename> file; see + below for details.</para> + <sect3 id="sec.tour-basic.username"> + <title>Creating a Mercurial configuration file</title> + + <para>To set a user name, use your favourite editor + to create a file called <filename + role="special">.hgrc</filename> in your home directory. + Mercurial will use this file to look up your personalised + configuration settings. The initial contents of your + <filename role="special">.hgrc</filename> should look like + this.</para> + <programlisting># This is a Mercurial configuration file. +[ui] +username = Firstname Lastname +<email.address@domain.net></programlisting> + + <para>The <quote><literal>[ui]</literal></quote> line begins a + <emphasis>section</emphasis> of the config file, so you can + read the <quote><literal>username = ...</literal></quote> + line as meaning <quote>set the value of the + <literal>username</literal> item in the + <literal>ui</literal> section</quote>. A section continues + until a new section begins, or the end of the file. + Mercurial ignores empty lines and treats any text from + <quote><literal>#</literal></quote> to the end of a line as + a comment.</para> + </sect3> + + <sect3> + <title>Choosing a user name</title> + + <para>You can use any text you like as the value of + the <literal>username</literal> config item, since this + information is for reading by other people, but for + interpreting by Mercurial. The convention that most + people follow is to use their name and email address, as + in the example above.</para> + <note> + <para>Mercurial's built-in web server obfuscates + email addresses, to make it more difficult for the email + harvesting tools that spammers use. This reduces the + likelihood that you'll start receiving more junk email + if you publish a Mercurial repository on the + web.</para></note> + + </sect3> + </sect2> + <sect2> + <title>Writing a commit message</title> + + <para>When we commit a change, Mercurial drops us into + a text editor, to enter a message that will describe the + modifications we've made in this changeset. This is called + the <emphasis>commit message</emphasis>. It will be a + record for readers of what we did and why, and it will be + printed by <command role="hg-cmd">hg log</command> after + we've finished committing.</para> + + &interaction.tour.commit; + + <para>The editor that the <command role="hg-cmd">hg + commit</command> command drops us into will contain an + empty line, followed by a number of lines starting with + <quote><literal>HG:</literal></quote>.</para> + + <programlisting>XXX fix this XXX</programlisting> + + <para>Mercurial ignores the lines that start with + <quote><literal>HG:</literal></quote>; it uses them only to + tell us which files it's recording changes to. Modifying or + deleting these lines has no effect.</para> + </sect2> + <sect2> + <title>Writing a good commit message</title> + + <para>Since <command role="hg-cmd">hg log</command> + only prints the first line of a commit message by default, + it's best to write a commit message whose first line stands + alone. Here's a real example of a commit message that + <emphasis>doesn't</emphasis> follow this guideline, and + hence has a summary that is not + readable.</para> + + <programlisting> +changeset: 73:584af0e231be +user: Censored Person <censored.person@example.org> +date: Tue Sep 26 21:37:07 2006 -0700 +summary: include buildmeister/commondefs. Add exports.</programlisting> + + <para>As far as the remainder of the contents of the + commit message are concerned, there are no hard-and-fast + rules. Mercurial itself doesn't interpret or care about the + contents of the commit message, though your project may have + policies that dictate a certain kind of + formatting.</para> + <para>My personal preference is for short, but + informative, commit messages that tell me something that I + can't figure out with a quick glance at the output of + <command role="hg-cmd">hg log + --patch</command>.</para> + </sect2> + <sect2> + <title>Aborting a commit</title> + + <para>If you decide that you don't want to commit + while in the middle of editing a commit message, simply exit + from your editor without saving the file that it's editing. + This will cause nothing to happen to either the repository + or the working directory.</para> + <para>If we run the <command role="hg-cmd">hg + commit</command> command without any arguments, it records + all of the changes we've made, as reported by <command + role="hg-cmd">hg status</command> and <command + role="hg-cmd">hg diff</command>.</para> + </sect2> + <sect2> + <title>Admiring our new handiwork</title> + + <para>Once we've finished the commit, we can use the + <command role="hg-cmd">hg tip</command> command to display + the changeset we just created. This command produces output + that is identical to <command role="hg-cmd">hg + log</command>, but it only displays the newest revision in + the repository.</para> + + &interaction.tour.tip; + + <para>We refer to + the newest revision in the repository as the tip revision, + or simply the tip.</para> + </sect2> + </sect1> + + <sect1> + <title>Sharing changes</title> + + <para>We mentioned earlier that repositories in + Mercurial are self-contained. This means that the changeset + we just created exists only in our <filename + class="directory">my-hello</filename> repository. Let's + look at a few ways that we can propagate this change into + other repositories.</para> + + <sect2 id="sec.tour.pull"> + <title>Pulling changes from another repository</title> + <para>To get started, let's clone our original + <filename class="directory">hello</filename> repository, + which does not contain the change we just committed. We'll + call our temporary repository <filename + class="directory">hello-pull</filename>.</para> + + &interaction.tour.clone-pull; + + <para>We'll use the <command role="hg-cmd">hg + pull</command> command to bring changes from <filename + class="directory">my-hello</filename> into <filename + class="directory">hello-pull</filename>. However, blindly + pulling unknown changes into a repository is a somewhat + scary prospect. Mercurial provides the <command + role="hg-cmd">hg incoming</command> command to tell us + what changes the <command role="hg-cmd">hg pull</command> + command <emphasis>would</emphasis> pull into the repository, + without actually pulling the changes in.</para> + + &interaction.tour.incoming; + + <para>(Of course, someone could + cause more changesets to appear in the repository that we + ran <command role="hg-cmd">hg incoming</command> in, before + we get a chance to <command role="hg-cmd">hg pull</command> + the changes, so that we could end up pulling changes that we + didn't expect.)</para> + + <para>Bringing changes into a repository is a simple + matter of running the <command role="hg-cmd">hg + pull</command> command, and telling it which repository to + pull from.</para> + + &interaction.tour.pull; + + <para>As you can see + from the before-and-after output of <command + role="hg-cmd">hg tip</command>, we have successfully + pulled changes into our repository. There remains one step + before we can see these changes in the working + directory.</para> + </sect2> + <sect2> + <title>Updating the working directory</title> + + <para>We have so far glossed over the relationship between a + repository and its working directory. The <command + role="hg-cmd">hg pull</command> command that we ran in + section <xref linkend="sec.tour.pull"/> brought changes + into the repository, but if we check, there's no sign of those + changes in the working directory. This is because <command + role="hg-cmd">hg pull</command> does not (by default) touch + the working directory. Instead, we use the <command + role="hg-cmd">hg update</command> command to do this.</para> + + &interaction.tour.update; + + <para>It might seem a bit strange that <command role="hg-cmd">hg + pull</command> doesn't update the working directory + automatically. There's actually a good reason for this: you + can use <command role="hg-cmd">hg update</command> to update + the working directory to the state it was in at <emphasis>any + revision</emphasis> in the history of the repository. If + you had the working directory updated to an old revision---to + hunt down the origin of a bug, say---and ran a <command + role="hg-cmd">hg pull</command> which automatically updated + the working directory to a new revision, you might not be + terribly happy.</para> + <para>However, since pull-then-update is such a common thing to + do, Mercurial lets you combine the two by passing the <option + role="hg-opt-pull">-u</option> option to <command + role="hg-cmd">hg pull</command>.</para> + + <para>If you look back at the output of <command + role="hg-cmd">hg pull</command> in section <xref + linkend="sec.tour.pull"/> when we ran it without <option + role="hg-opt-pull">-u</option>, you can see that it printed + a helpful reminder that we'd have to take an explicit step to + update the working directory:</para> + + <!-- &interaction.xxx.fixme; --> + + <para>To find out what revision the working directory is at, use + the <command role="hg-cmd">hg parents</command> + command.</para> + + &interaction.tour.parents; + + <para>If you look back at figure <xref + endterm="fig.tour-basic.history.caption" + linkend="fig.tour-basic.history"/>, + you'll see arrows connecting each changeset. The node that + the arrow leads <emphasis>from</emphasis> in each case is a + parent, and the node that the arrow leads + <emphasis>to</emphasis> is its child. The working directory + has a parent in just the same way; this is the changeset that + the working directory currently contains.</para> + + <para>To update the working directory to a particular revision, + + give a revision number or changeset ID to the <command + role="hg-cmd">hg update</command> command.</para> + + &interaction.tour.older; + + <para>If you omit an explicit revision, <command + role="hg-cmd">hg update</command> will update to the tip + revision, as shown by the second call to <command + role="hg-cmd">hg update</command> in the example + above.</para> + </sect2> + + <sect2> + <title>Pushing changes to another repository</title> + + <para>Mercurial lets us push changes to another + repository, from the repository we're currently visiting. + As with the example of <command role="hg-cmd">hg + pull</command> above, we'll create a temporary repository + to push our changes into.</para> + + &interaction.tour.clone-push; + + <para>The <command role="hg-cmd">hg outgoing</command> command + tells us what changes would be pushed into another + repository.</para> + + &interaction.tour.outgoing; + + <para>And the + <command role="hg-cmd">hg push</command> command does the + actual push.</para> + + &interaction.tour.push; + + <para>As with + <command role="hg-cmd">hg pull</command>, the <command + role="hg-cmd">hg push</command> command does not update + the working directory in the repository that it's pushing + changes into. (Unlike <command role="hg-cmd">hg + pull</command>, <command role="hg-cmd">hg push</command> + does not provide a <literal>-u</literal> option that updates + the other repository's working directory.)</para> + + <para>What happens if we try to pull or push changes + and the receiving repository already has those changes? + Nothing too exciting.</para> + + &interaction.tour.push.nothing; + </sect2> + <sect2> + <title>Sharing changes over a network</title> + + <para>The commands we have covered in the previous few + sections are not limited to working with local repositories. + Each works in exactly the same fashion over a network + connection; simply pass in a URL instead of a local + path.</para> + + &interaction.tour.outgoing.net; + + <para>In this example, we + can see what changes we could push to the remote repository, + but the repository is understandably not set up to let + anonymous users push to it.</para> + + &interaction.tour.push.net; + </sect2> + </sect1> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- a/en/ch02-tour-basic.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,862 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.tour-basic"> - <?dbhtml filename="a-tour-of-mercurial-the-basics.html"?> - <title>A tour of Mercurial: the basics</title> - - <sect1 id="sec.tour.install"> - <title>Installing Mercurial on your system</title> - - <para>Prebuilt binary packages of Mercurial are available for - every popular operating system. These make it easy to start - using Mercurial on your computer immediately.</para> - - <sect2> - <title>Linux</title> - - <para>Because each Linux distribution has its own packaging - tools, policies, and rate of development, it's difficult to - give a comprehensive set of instructions on how to install - Mercurial binaries. The version of Mercurial that you will - end up with can vary depending on how active the person is who - maintains the package for your distribution.</para> - - <para>To keep things simple, I will focus on installing - Mercurial from the command line under the most popular Linux - distributions. Most of these distributions provide graphical - package managers that will let you install Mercurial with a - single click; the package name to look for is - <literal>mercurial</literal>.</para> - - <itemizedlist> - <listitem><para>Debian:</para> - <programlisting>apt-get install mercurial</programlisting></listitem> - <listitem><para>Fedora Core:</para> - <programlisting>yum install mercurial</programlisting></listitem> - <listitem><para>Gentoo:</para> - <programlisting>emerge mercurial</programlisting></listitem> - <listitem><para>OpenSUSE:</para> - <programlisting>yum install mercurial</programlisting></listitem> - <listitem><para>Ubuntu: Ubuntu's Mercurial package is based on - Debian's. To install it, run the following - command.</para> - <programlisting>apt-get install mercurial</programlisting></listitem> - </itemizedlist> - - </sect2> - <sect2> - <title>Solaris</title> - - <para>SunFreeWare, at <ulink - url="http://www.sunfreeware.com">http://www.sunfreeware.com</ulink>, - is a good source for a large number of pre-built Solaris - packages for 32 and 64 bit Intel and Sparc architectures, - including current versions of Mercurial.</para> - - </sect2> - <sect2> - <title>Mac OS X</title> - - <para>Lee Cantey publishes an installer of Mercurial for Mac OS - X at <ulink - url="http://mercurial.berkwood.com">http://mercurial.berkwood.com</ulink>. - This package works on both Intel- and Power-based Macs. Before - you can use it, you must install a compatible version of - Universal MacPython <citation>web:macpython</citation>. This - is easy to do; simply follow the instructions on Lee's - site.</para> - - <para>It's also possible to install Mercurial using Fink or - MacPorts, two popular free package managers for Mac OS X. If - you have Fink, use <command>sudo apt-get install - mercurial-py25</command>. If MacPorts, <command>sudo port - install mercurial</command>.</para> - - </sect2> - <sect2> - <title>Windows</title> - - <para>Lee Cantey publishes an installer of Mercurial for Windows - at <ulink - url="http://mercurial.berkwood.com">http://mercurial.berkwood.com</ulink>. - This package has no external dependencies; it <quote>just - works</quote>.</para> - - <note> - <para> The Windows version of Mercurial does not - automatically convert line endings between Windows and Unix - styles. If you want to share work with Unix users, you must - do a little additional configuration work. XXX Flesh this - out.</para> - </note> - - </sect2> - </sect1> - <sect1> - <title>Getting started</title> - - <para>To begin, we'll use the <command role="hg-cmd">hg - version</command> command to find out whether Mercurial is - actually installed properly. The actual version information - that it prints isn't so important; it's whether it prints - anything at all that we care about.</para> - - &interaction.tour.version; - - <sect2> - <title>Built-in help</title> - - <para>Mercurial provides a built-in help system. This is - invaluable for those times when you find yourself stuck - trying to remember how to run a command. If you are - completely stuck, simply run <command role="hg-cmd">hg - help</command>; it will print a brief list of commands, - along with a description of what each does. If you ask for - help on a specific command (as below), it prints more - detailed information.</para> - - &interaction.tour.help; - - <para>For a more impressive level of detail (which you won't - usually need) run <command role="hg-cmd">hg help <option - role="hg-opt-global">-v</option></command>. The <option - role="hg-opt-global">-v</option> option is short for - <option role="hg-opt-global">--verbose</option>, and tells - Mercurial to print more information than it usually - would.</para> - - </sect2> - </sect1> - <sect1> - <title>Working with a repository</title> - - <para>In Mercurial, everything happens inside a - <emphasis>repository</emphasis>. The repository for a project - contains all of the files that <quote>belong to</quote> that - project, along with a historical record of the project's - files.</para> - - <para>There's nothing particularly magical about a repository; it - is simply a directory tree in your filesystem that Mercurial - treats as special. You can rename or delete a repository any - time you like, using either the command line or your file - browser.</para> - - <sect2> - <title>Making a local copy of a repository</title> - - <para><emphasis>Copying</emphasis> a repository is just a little - bit special. While you could use a normal file copying - command to make a copy of a repository, it's best to use a - built-in command that Mercurial provides. This command is - called <command role="hg-cmd">hg clone</command>, because it - creates an identical copy of an existing repository.</para> - - &interaction.tour.clone; - - <para>If our clone succeeded, we should now have a local - directory called <filename class="directory">hello</filename>. - This directory will contain some files.</para> - - &interaction.tour.ls; - - <para>These files have the same contents and history in our - repository as they do in the repository we cloned.</para> - - <para>Every Mercurial repository is complete, self-contained, - and independent. It contains its own private copy of a - project's files and history. A cloned repository remembers - the location of the repository it was cloned from, but it does - not communicate with that repository, or any other, unless you - tell it to.</para> - - <para>What this means for now is that we're free to experiment - with our repository, safe in the knowledge that it's a private - <quote>sandbox</quote> that won't affect anyone else.</para> - - </sect2> - <sect2> - <title>What's in a repository?</title> - - <para>When we take a more detailed look inside a repository, we - can see that it contains a directory named <filename - class="directory">.hg</filename>. This is where Mercurial - keeps all of its metadata for the repository.</para> - - &interaction.tour.ls-a; - - <para>The contents of the <filename - class="directory">.hg</filename> directory and its - subdirectories are private to Mercurial. Every other file and - directory in the repository is yours to do with as you - please.</para> - - <para>To introduce a little terminology, the <filename - class="directory">.hg</filename> directory is the - <quote>real</quote> repository, and all of the files and - directories that coexist with it are said to live in the - <emphasis>working directory</emphasis>. An easy way to - remember the distinction is that the - <emphasis>repository</emphasis> contains the - <emphasis>history</emphasis> of your project, while the - <emphasis>working directory</emphasis> contains a - <emphasis>snapshot</emphasis> of your project at a particular - point in history.</para> - - </sect2> - </sect1> - <sect1> - <title>A tour through history</title> - - <para>One of the first things we might want to do with a new, - unfamiliar repository is understand its history. The <command - role="hg-cmd">hg log</command> command gives us a view of - history.</para> - - &interaction.tour.log; - - <para>By default, this command prints a brief paragraph of output - for each change to the project that was recorded. In Mercurial - terminology, we call each of these recorded events a - <emphasis>changeset</emphasis>, because it can contain a record - of changes to several files.</para> - - <para>The fields in a record of output from <command - role="hg-cmd">hg log</command> are as follows.</para> - <itemizedlist> - <listitem><para><literal>changeset</literal>: This field has the - format of a number, followed by a colon, followed by a - hexadecimal string. These are - <emphasis>identifiers</emphasis> for the changeset. There - are two identifiers because the number is shorter and easier - to type than the hex string.</para></listitem> - <listitem><para><literal>user</literal>: The identity of the - person who created the changeset. This is a free-form - field, but it most often contains a person's name and email - address.</para></listitem> - <listitem><para><literal>date</literal>: The date and time on - which the changeset was created, and the timezone in which - it was created. (The date and time are local to that - timezone; they display what time and date it was for the - person who created the changeset.)</para></listitem> - <listitem><para><literal>summary</literal>: The first line of - the text message that the creator of the changeset entered - to describe the changeset.</para></listitem></itemizedlist> - <para>The default output printed by <command role="hg-cmd">hg - log</command> is purely a summary; it is missing a lot of - detail.</para> - - <para>Figure <xref endterm="fig.tour-basic.history.caption" - linkend="fig.tour-basic.history"/> provides a - graphical representation of the history of the <filename - class="directory">hello</filename> repository, to make it a - little easier to see which direction history is - <quote>flowing</quote> in. We'll be returning to this figure - several times in this chapter and the chapter that - follows.</para> - - <informalfigure id="fig.tour-basic.history"> - <mediaobject> - <imageobject><imagedata fileref="images/tour-history.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.tour-basic.history.caption">Graphical history of - the <filename class="directory">hello</filename> repository</para> - </caption> - </mediaobject> - </informalfigure> - - <sect2> - <title>Changesets, revisions, and talking to other - people</title> - - <para>As English is a notoriously sloppy language, and computer - science has a hallowed history of terminological confusion - (why use one term when four will do?), revision control has a - variety of words and phrases that mean the same thing. If you - are talking about Mercurial history with other people, you - will find that the word <quote>changeset</quote> is often - compressed to <quote>change</quote> or (when written) - <quote>cset</quote>, and sometimes a changeset is referred to - as a <quote>revision</quote> or a <quote>rev</quote>.</para> - - <para>While it doesn't matter what <emphasis>word</emphasis> you - use to refer to the concept of <quote>a changeset</quote>, the - <emphasis>identifier</emphasis> that you use to refer to - <quote>a <emphasis>specific</emphasis> changeset</quote> is of - great importance. Recall that the <literal>changeset</literal> - field in the output from <command role="hg-cmd">hg - log</command> identifies a changeset using both a number and - a hexadecimal string.</para> - <itemizedlist> - <listitem><para>The revision number is <emphasis>only valid in - that repository</emphasis>,</para></listitem> - <listitem><para>while the hex string is the - <emphasis>permanent, unchanging identifier</emphasis> that - will always identify that exact changeset in - <emphasis>every</emphasis> copy of the - repository.</para></listitem></itemizedlist> - <para>This distinction is important. If you send someone an - email talking about <quote>revision 33</quote>, there's a high - likelihood that their revision 33 will <emphasis>not be the - same</emphasis> as yours. The reason for this is that a - revision number depends on the order in which changes arrived - in a repository, and there is no guarantee that the same - changes will happen in the same order in different - repositories. Three changes $a,b,c$ can easily appear in one - repository as $0,1,2$, while in another as $1,0,2$.</para> - - <para>Mercurial uses revision numbers purely as a convenient - shorthand. If you need to discuss a changeset with someone, - or make a record of a changeset for some other reason (for - example, in a bug report), use the hexadecimal - identifier.</para> - - </sect2> - <sect2> - <title>Viewing specific revisions</title> - - <para>To narrow the output of <command role="hg-cmd">hg - log</command> down to a single revision, use the <option - role="hg-opt-log">-r</option> (or <option - role="hg-opt-log">--rev</option>) option. You can use - either a revision number or a long-form changeset identifier, - and you can provide as many revisions as you want.</para> - - &interaction.tour.log-r; - - <para>If you want to see the history of several revisions - without having to list each one, you can use <emphasis>range - notation</emphasis>; this lets you express the idea <quote>I - want all revisions between <literal>abc</literal> and - <literal>def</literal>, inclusive</quote>.</para> - - &interaction.tour.log.range; - - <para>Mercurial also honours the order in which you specify - revisions, so <command role="hg-cmd">hg log -r 2:4</command> - prints 2, 3, and 4. while <command role="hg-cmd">hg log -r - 4:2</command> prints 4, 3, and 2.</para> - - </sect2> - <sect2> - <title>More detailed information</title> - - <para>While the summary information printed by <command - role="hg-cmd">hg log</command> is useful if you already know - what you're looking for, you may need to see a complete - description of the change, or a list of the files changed, if - you're trying to decide whether a changeset is the one you're - looking for. The <command role="hg-cmd">hg log</command> - command's <option role="hg-opt-global">-v</option> (or <option - role="hg-opt-global">--verbose</option>) option gives you - this extra detail.</para> - - &interaction.tour.log-v; - - <para>If you want to see both the description and content of a - change, add the <option role="hg-opt-log">-p</option> (or - <option role="hg-opt-log">--patch</option>) option. This - displays the content of a change as a <emphasis>unified - diff</emphasis> (if you've never seen a unified diff before, - see section <xref linkend="sec.mq.patch"/> for an - overview).</para> - - &interaction.tour.log-vp; - - </sect2> - </sect1> - <sect1> - <title>All about command options</title> - - <para>Let's take a brief break from exploring Mercurial commands - to discuss a pattern in the way that they work; you may find - this useful to keep in mind as we continue our tour.</para> - - <para>Mercurial has a consistent and straightforward approach to - dealing with the options that you can pass to commands. It - follows the conventions for options that are common to modern - Linux and Unix systems.</para> - <itemizedlist> - <listitem><para>Every option has a long name. For example, as - we've already seen, the <command role="hg-cmd">hg - log</command> command accepts a <option - role="hg-opt-log">--rev</option> option.</para></listitem> - <listitem><para>Most options have short names, too. Instead of - <option role="hg-opt-log">--rev</option>, we can use <option - role="hg-opt-log">-r</option>. (The reason that some - options don't have short names is that the options in - question are rarely used.)</para></listitem> - <listitem><para>Long options start with two dashes (e.g. <option - role="hg-opt-log">--rev</option>), while short options - start with one (e.g. <option - role="hg-opt-log">-r</option>).</para></listitem> - <listitem><para>Option naming and usage is consistent across - commands. For example, every command that lets you specify - a changeset ID or revision number accepts both <option - role="hg-opt-log">-r</option> and <option - role="hg-opt-log">--rev</option> - arguments.</para></listitem></itemizedlist> - <para>In the examples throughout this book, I use short options - instead of long. This just reflects my own preference, so don't - read anything significant into it.</para> - - <para>Most commands that print output of some kind will print more - output when passed a <option role="hg-opt-global">-v</option> - (or <option role="hg-opt-global">--verbose</option>) option, and - less when passed <option role="hg-opt-global">-q</option> (or - <option role="hg-opt-global">--quiet</option>).</para> - - </sect1> - <sect1> - <title>Making and reviewing changes</title> - - <para>Now that we have a grasp of viewing history in Mercurial, - let's take a look at making some changes and examining - them.</para> - - <para>The first thing we'll do is isolate our experiment in a - repository of its own. We use the <command role="hg-cmd">hg - clone</command> command, but we don't need to clone a copy of - the remote repository. Since we already have a copy of it - locally, we can just clone that instead. This is much faster - than cloning over the network, and cloning a local repository - uses less disk space in most cases, too.</para> - - &interaction.tour.reclone; - - <para>As an aside, it's often good practice to keep a - <quote>pristine</quote> copy of a remote repository around, - which you can then make temporary clones of to create sandboxes - for each task you want to work on. This lets you work on - multiple tasks in parallel, each isolated from the others until - it's complete and you're ready to integrate it back. Because - local clones are so cheap, there's almost no overhead to cloning - and destroying repositories whenever you want.</para> - - <para>In our <filename class="directory">my-hello</filename> - repository, we have a file <filename>hello.c</filename> that - contains the classic <quote>hello, world</quote> program. Let's - use the ancient and venerable <command>sed</command> command to - edit this file so that it prints a second line of output. (I'm - only using <command>sed</command> to do this because it's easy - to write a scripted example this way. Since you're not under - the same constraint, you probably won't want to use - <command>sed</command>; simply use your preferred text editor to - do the same thing.)</para> - - &interaction.tour.sed; - - <para>Mercurial's <command role="hg-cmd">hg status</command> - command will tell us what Mercurial knows about the files in the - repository.</para> - - &interaction.tour.status; - - <para>The <command role="hg-cmd">hg status</command> command - prints no output for some files, but a line starting with - <quote><literal>M</literal></quote> for - <filename>hello.c</filename>. Unless you tell it to, <command - role="hg-cmd">hg status</command> will not print any output - for files that have not been modified.</para> - - <para>The <quote><literal>M</literal></quote> indicates that - Mercurial has noticed that we modified - <filename>hello.c</filename>. We didn't need to - <emphasis>inform</emphasis> Mercurial that we were going to - modify the file before we started, or that we had modified the - file after we were done; it was able to figure this out - itself.</para> - - <para>It's a little bit helpful to know that we've modified - <filename>hello.c</filename>, but we might prefer to know - exactly <emphasis>what</emphasis> changes we've made to it. To - do this, we use the <command role="hg-cmd">hg diff</command> - command.</para> - - &interaction.tour.diff; - - </sect1> - <sect1> - <title>Recording changes in a new changeset</title> - - <para>We can modify files, build and test our changes, and use - <command role="hg-cmd">hg status</command> and <command - role="hg-cmd">hg diff</command> to review our changes, until - we're satisfied with what we've done and arrive at a natural - stopping point where we want to record our work in a new - changeset.</para> - - <para>The <command role="hg-cmd">hg commit</command> command lets - us create a new changeset; we'll usually refer to this as - <quote>making a commit</quote> or - <quote>committing</quote>.</para> - - <sect2> - <title>Setting up a username</title> - - <para>When you try to run <command role="hg-cmd">hg - commit</command> for the first time, it is not guaranteed to - succeed. Mercurial records your name and address with each - change that you commit, so that you and others will later be - able to tell who made each change. Mercurial tries to - automatically figure out a sensible username to commit the - change with. It will attempt each of the following methods, - in order:</para> - <orderedlist> - <listitem><para>If you specify a <option - role="hg-opt-commit">-u</option> option to the <command - role="hg-cmd">hg commit</command> command on the command - line, followed by a username, this is always given the - highest precedence.</para></listitem> - <listitem><para>If you have set the <envar>HGUSER</envar> - environment variable, this is checked - next.</para></listitem> - <listitem><para>If you create a file in your home directory - called <filename role="special">.hgrc</filename>, with a - <envar role="rc-item-ui">username</envar> entry, that will - be used next. To see what the contents of this file - should look like, refer to section <xref - linkend="sec.tour-basic.username"/> - below.</para></listitem> - <listitem><para>If you have set the <envar>EMAIL</envar> - environment variable, this will be used - next.</para></listitem> - <listitem><para>Mercurial will query your system to find out - your local user name and host name, and construct a - username from these components. Since this often results - in a username that is not very useful, it will print a - warning if it has to do - this.</para></listitem> - </orderedlist> - <para>If all of these mechanisms fail, Mercurial will - fail, printing an error message. In this case, it will not - let you commit until you set up a - username.</para> - <para>You should think of the <envar>HGUSER</envar> environment - variable and the <option role="hg-opt-commit">-u</option> - option to the <command role="hg-cmd">hg commit</command> - command as ways to <emphasis>override</emphasis> Mercurial's - default selection of username. For normal use, the simplest - and most robust way to set a username for yourself is by - creating a <filename role="special">.hgrc</filename> file; see - below for details.</para> - <sect3 id="sec.tour-basic.username"> - <title>Creating a Mercurial configuration file</title> - - <para>To set a user name, use your favourite editor - to create a file called <filename - role="special">.hgrc</filename> in your home directory. - Mercurial will use this file to look up your personalised - configuration settings. The initial contents of your - <filename role="special">.hgrc</filename> should look like - this.</para> - <programlisting># This is a Mercurial configuration file. -[ui] -username = Firstname Lastname -<email.address@domain.net></programlisting> - - <para>The <quote><literal>[ui]</literal></quote> line begins a - <emphasis>section</emphasis> of the config file, so you can - read the <quote><literal>username = ...</literal></quote> - line as meaning <quote>set the value of the - <literal>username</literal> item in the - <literal>ui</literal> section</quote>. A section continues - until a new section begins, or the end of the file. - Mercurial ignores empty lines and treats any text from - <quote><literal>#</literal></quote> to the end of a line as - a comment.</para> - </sect3> - - <sect3> - <title>Choosing a user name</title> - - <para>You can use any text you like as the value of - the <literal>username</literal> config item, since this - information is for reading by other people, but for - interpreting by Mercurial. The convention that most - people follow is to use their name and email address, as - in the example above.</para> - <note> - <para>Mercurial's built-in web server obfuscates - email addresses, to make it more difficult for the email - harvesting tools that spammers use. This reduces the - likelihood that you'll start receiving more junk email - if you publish a Mercurial repository on the - web.</para></note> - - </sect3> - </sect2> - <sect2> - <title>Writing a commit message</title> - - <para>When we commit a change, Mercurial drops us into - a text editor, to enter a message that will describe the - modifications we've made in this changeset. This is called - the <emphasis>commit message</emphasis>. It will be a - record for readers of what we did and why, and it will be - printed by <command role="hg-cmd">hg log</command> after - we've finished committing.</para> - - &interaction.tour.commit; - - <para>The editor that the <command role="hg-cmd">hg - commit</command> command drops us into will contain an - empty line, followed by a number of lines starting with - <quote><literal>HG:</literal></quote>.</para> - - <programlisting>XXX fix this XXX</programlisting> - - <para>Mercurial ignores the lines that start with - <quote><literal>HG:</literal></quote>; it uses them only to - tell us which files it's recording changes to. Modifying or - deleting these lines has no effect.</para> - </sect2> - <sect2> - <title>Writing a good commit message</title> - - <para>Since <command role="hg-cmd">hg log</command> - only prints the first line of a commit message by default, - it's best to write a commit message whose first line stands - alone. Here's a real example of a commit message that - <emphasis>doesn't</emphasis> follow this guideline, and - hence has a summary that is not - readable.</para> - - <programlisting> -changeset: 73:584af0e231be -user: Censored Person <censored.person@example.org> -date: Tue Sep 26 21:37:07 2006 -0700 -summary: include buildmeister/commondefs. Add exports.</programlisting> - - <para>As far as the remainder of the contents of the - commit message are concerned, there are no hard-and-fast - rules. Mercurial itself doesn't interpret or care about the - contents of the commit message, though your project may have - policies that dictate a certain kind of - formatting.</para> - <para>My personal preference is for short, but - informative, commit messages that tell me something that I - can't figure out with a quick glance at the output of - <command role="hg-cmd">hg log - --patch</command>.</para> - </sect2> - <sect2> - <title>Aborting a commit</title> - - <para>If you decide that you don't want to commit - while in the middle of editing a commit message, simply exit - from your editor without saving the file that it's editing. - This will cause nothing to happen to either the repository - or the working directory.</para> - <para>If we run the <command role="hg-cmd">hg - commit</command> command without any arguments, it records - all of the changes we've made, as reported by <command - role="hg-cmd">hg status</command> and <command - role="hg-cmd">hg diff</command>.</para> - </sect2> - <sect2> - <title>Admiring our new handiwork</title> - - <para>Once we've finished the commit, we can use the - <command role="hg-cmd">hg tip</command> command to display - the changeset we just created. This command produces output - that is identical to <command role="hg-cmd">hg - log</command>, but it only displays the newest revision in - the repository.</para> - - &interaction.tour.tip; - - <para>We refer to - the newest revision in the repository as the tip revision, - or simply the tip.</para> - </sect2> - </sect1> - - <sect1> - <title>Sharing changes</title> - - <para>We mentioned earlier that repositories in - Mercurial are self-contained. This means that the changeset - we just created exists only in our <filename - class="directory">my-hello</filename> repository. Let's - look at a few ways that we can propagate this change into - other repositories.</para> - - <sect2 id="sec.tour.pull"> - <title>Pulling changes from another repository</title> - <para>To get started, let's clone our original - <filename class="directory">hello</filename> repository, - which does not contain the change we just committed. We'll - call our temporary repository <filename - class="directory">hello-pull</filename>.</para> - - &interaction.tour.clone-pull; - - <para>We'll use the <command role="hg-cmd">hg - pull</command> command to bring changes from <filename - class="directory">my-hello</filename> into <filename - class="directory">hello-pull</filename>. However, blindly - pulling unknown changes into a repository is a somewhat - scary prospect. Mercurial provides the <command - role="hg-cmd">hg incoming</command> command to tell us - what changes the <command role="hg-cmd">hg pull</command> - command <emphasis>would</emphasis> pull into the repository, - without actually pulling the changes in.</para> - - &interaction.tour.incoming; - - <para>(Of course, someone could - cause more changesets to appear in the repository that we - ran <command role="hg-cmd">hg incoming</command> in, before - we get a chance to <command role="hg-cmd">hg pull</command> - the changes, so that we could end up pulling changes that we - didn't expect.)</para> - - <para>Bringing changes into a repository is a simple - matter of running the <command role="hg-cmd">hg - pull</command> command, and telling it which repository to - pull from.</para> - - &interaction.tour.pull; - - <para>As you can see - from the before-and-after output of <command - role="hg-cmd">hg tip</command>, we have successfully - pulled changes into our repository. There remains one step - before we can see these changes in the working - directory.</para> - </sect2> - <sect2> - <title>Updating the working directory</title> - - <para>We have so far glossed over the relationship between a - repository and its working directory. The <command - role="hg-cmd">hg pull</command> command that we ran in - section <xref linkend="sec.tour.pull"/> brought changes - into the repository, but if we check, there's no sign of those - changes in the working directory. This is because <command - role="hg-cmd">hg pull</command> does not (by default) touch - the working directory. Instead, we use the <command - role="hg-cmd">hg update</command> command to do this.</para> - - &interaction.tour.update; - - <para>It might seem a bit strange that <command role="hg-cmd">hg - pull</command> doesn't update the working directory - automatically. There's actually a good reason for this: you - can use <command role="hg-cmd">hg update</command> to update - the working directory to the state it was in at <emphasis>any - revision</emphasis> in the history of the repository. If - you had the working directory updated to an old revision---to - hunt down the origin of a bug, say---and ran a <command - role="hg-cmd">hg pull</command> which automatically updated - the working directory to a new revision, you might not be - terribly happy.</para> - <para>However, since pull-then-update is such a common thing to - do, Mercurial lets you combine the two by passing the <option - role="hg-opt-pull">-u</option> option to <command - role="hg-cmd">hg pull</command>.</para> - - <para>If you look back at the output of <command - role="hg-cmd">hg pull</command> in section <xref - linkend="sec.tour.pull"/> when we ran it without <option - role="hg-opt-pull">-u</option>, you can see that it printed - a helpful reminder that we'd have to take an explicit step to - update the working directory:</para> - - <!-- &interaction.xxx.fixme; --> - - <para>To find out what revision the working directory is at, use - the <command role="hg-cmd">hg parents</command> - command.</para> - - &interaction.tour.parents; - - <para>If you look back at figure <xref - endterm="fig.tour-basic.history.caption" - linkend="fig.tour-basic.history"/>, - you'll see arrows connecting each changeset. The node that - the arrow leads <emphasis>from</emphasis> in each case is a - parent, and the node that the arrow leads - <emphasis>to</emphasis> is its child. The working directory - has a parent in just the same way; this is the changeset that - the working directory currently contains.</para> - - <para>To update the working directory to a particular revision, - - give a revision number or changeset ID to the <command - role="hg-cmd">hg update</command> command.</para> - - &interaction.tour.older; - - <para>If you omit an explicit revision, <command - role="hg-cmd">hg update</command> will update to the tip - revision, as shown by the second call to <command - role="hg-cmd">hg update</command> in the example - above.</para> - </sect2> - - <sect2> - <title>Pushing changes to another repository</title> - - <para>Mercurial lets us push changes to another - repository, from the repository we're currently visiting. - As with the example of <command role="hg-cmd">hg - pull</command> above, we'll create a temporary repository - to push our changes into.</para> - - &interaction.tour.clone-push; - - <para>The <command role="hg-cmd">hg outgoing</command> command - tells us what changes would be pushed into another - repository.</para> - - &interaction.tour.outgoing; - - <para>And the - <command role="hg-cmd">hg push</command> command does the - actual push.</para> - - &interaction.tour.push; - - <para>As with - <command role="hg-cmd">hg pull</command>, the <command - role="hg-cmd">hg push</command> command does not update - the working directory in the repository that it's pushing - changes into. (Unlike <command role="hg-cmd">hg - pull</command>, <command role="hg-cmd">hg push</command> - does not provide a <literal>-u</literal> option that updates - the other repository's working directory.)</para> - - <para>What happens if we try to pull or push changes - and the receiving repository already has those changes? - Nothing too exciting.</para> - - &interaction.tour.push.nothing; - </sect2> - <sect2> - <title>Sharing changes over a network</title> - - <para>The commands we have covered in the previous few - sections are not limited to working with local repositories. - Each works in exactly the same fashion over a network - connection; simply pass in a URL instead of a local - path.</para> - - &interaction.tour.outgoing.net; - - <para>In this example, we - can see what changes we could push to the remote repository, - but the repository is understandably not set up to let - anonymous users push to it.</para> - - &interaction.tour.push.net; - </sect2> - </sect1> -</chapter> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch02-tour-merge.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,401 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.tour-merge"> + <?dbhtml filename="a-tour-of-mercurial-merging-work.html"?> + <title>A tour of Mercurial: merging work</title> + + <para>We've now covered cloning a repository, making changes in a + repository, and pulling or pushing changes from one repository + into another. Our next step is <emphasis>merging</emphasis> + changes from separate repositories.</para> + + <sect1> + <title>Merging streams of work</title> + + <para>Merging is a fundamental part of working with a distributed + revision control tool.</para> + <itemizedlist> + <listitem><para>Alice and Bob each have a personal copy of a + repository for a project they're collaborating on. Alice + fixes a bug in her repository; Bob adds a new feature in + his. They want the shared repository to contain both the + bug fix and the new feature.</para> + </listitem> + <listitem><para>I frequently work on several different tasks for + a single project at once, each safely isolated in its own + repository. Working this way means that I often need to + merge one piece of my own work with another.</para> + </listitem></itemizedlist> + + <para>Because merging is such a common thing to need to do, + Mercurial makes it easy. Let's walk through the process. We'll + begin by cloning yet another repository (see how often they + spring up?) and making a change in it.</para> + + &interaction.tour.merge.clone; + + <para>We should now have two copies of + <filename>hello.c</filename> with different contents. The + histories of the two repositories have also diverged, as + illustrated in figure <xref endterm="fig.tour-merge.sep-repos.caption" + linkend="fig.tour-merge.sep-repos"/>.</para> + + &interaction.tour.merge.cat; + + <informalfigure id="fig.tour-merge.sep-repos"> + <mediaobject> + <imageobject><imagedata fileref="images/tour-merge-sep-repos.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.tour-merge.sep-repos.caption">Divergent recent + histories of the <filename + class="directory">my-hello</filename> and <filename + class="directory">my-new-hello</filename> + repositories</para></caption> + </mediaobject> + </informalfigure> + + <para>We already know that pulling changes from our <filename + class="directory">my-hello</filename> repository will have no + effect on the working directory.</para> + + &interaction.tour.merge.pull; + + <para>However, the <command role="hg-cmd">hg pull</command> + command says something about <quote>heads</quote>.</para> + + <sect2> + <title>Head changesets</title> + + <para>A head is a change that has no descendants, or children, + as they're also known. The tip revision is thus a head, + because the newest revision in a repository doesn't have any + children, but a repository can contain more than one + head.</para> + + <informalfigure id="fig.tour-merge.pull"> + <mediaobject> + <imageobject><imagedata fileref="images/tour-merge-pull.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.tour-merge.pull.caption">Repository contents after + pulling from <filename class="directory">my-hello</filename> into + <filename class="directory">my-new-hello</filename></para></caption> + </mediaobject> + </informalfigure> + + <para>In figure <xref endterm="fig.tour-merge.pull.caption" + linkend="fig.tour-merge.pull"/>, you can + see the effect of the pull from <filename + class="directory">my-hello</filename> into <filename + class="directory">my-new-hello</filename>. The history that + was already present in <filename + class="directory">my-new-hello</filename> is untouched, but + a new revision has been added. By referring to figure <xref + endterm="fig.tour-merge.sep-repos.caption" + linkend="fig.tour-merge.sep-repos"/>, we can see that the + <emphasis>changeset ID</emphasis> remains the same in the new + repository, but the <emphasis>revision number</emphasis> has + changed. (This, incidentally, is a fine example of why it's + not safe to use revision numbers when discussing changesets.) + We can view the heads in a repository using the <command + role="hg-cmd">hg heads</command> command.</para> + + &interaction.tour.merge.heads; + + </sect2> + <sect2> + <title>Performing the merge</title> + + <para>What happens if we try to use the normal <command + role="hg-cmd">hg update</command> command to update to the + new tip?</para> + + &interaction.tour.merge.update; + + <para>Mercurial is telling us that the <command role="hg-cmd">hg + update</command> command won't do a merge; it won't update + the working directory when it thinks we might be wanting to do + a merge, unless we force it to do so. Instead, we use the + <command role="hg-cmd">hg merge</command> command to merge the + two heads.</para> + + &interaction.tour.merge.merge; + + <informalfigure id="fig.tour-merge.merge"> + <mediaobject> + <imageobject><imagedata fileref="images/tour-merge-merge.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.tour-merge.merge.caption">Working directory and + repository during merge, and following commit</para></caption> + </mediaobject> + </informalfigure> + + <para>This updates the working directory so that it contains + changes from <emphasis>both</emphasis> heads, which is + reflected in both the output of <command role="hg-cmd">hg + parents</command> and the contents of + <filename>hello.c</filename>.</para> + + &interaction.tour.merge.parents; + + </sect2> + <sect2> + <title>Committing the results of the merge</title> + + <para>Whenever we've done a merge, <command role="hg-cmd">hg + parents</command> will display two parents until we <command + role="hg-cmd">hg commit</command> the results of the + merge.</para> + + &interaction.tour.merge.commit; + + <para>We now have a new tip revision; notice that it has + <emphasis>both</emphasis> of our former heads as its parents. + These are the same revisions that were previously displayed by + <command role="hg-cmd">hg parents</command>.</para> + + &interaction.tour.merge.tip; + + <para>In figure <xref endterm="fig.tour-merge.merge.caption" + linkend="fig.tour-merge.merge"/>, you can see a + representation of what happens to the working directory during + the merge, and how this affects the repository when the commit + happens. During the merge, the working directory has two + parent changesets, and these become the parents of the new + changeset.</para> + + </sect2> + </sect1> + <sect1> + <title>Merging conflicting changes</title> + + <para>Most merges are simple affairs, but sometimes you'll find + yourself merging changes where each modifies the same portions + of the same files. Unless both modifications are identical, + this results in a <emphasis>conflict</emphasis>, where you have + to decide how to reconcile the different changes into something + coherent.</para> + + <informalfigure id="fig.tour-merge.conflict"> + <mediaobject> + <imageobject><imagedata fileref="images/tour-merge-conflict.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.tour-merge.conflict.caption">Conflicting + changes to a document</para></caption> + </mediaobject> + </informalfigure> + + <para>Figure <xref endterm="fig.tour-merge.conflict.caption" + linkend="fig.tour-merge.conflict"/> illustrates + an instance of two conflicting changes to a document. We + started with a single version of the file; then we made some + changes; while someone else made different changes to the same + text. Our task in resolving the conflicting changes is to + decide what the file should look like.</para> + + <para>Mercurial doesn't have a built-in facility for handling + conflicts. Instead, it runs an external program called + <command>hgmerge</command>. This is a shell script that is + bundled with Mercurial; you can change it to behave however you + please. What it does by default is try to find one of several + different merging tools that are likely to be installed on your + system. It first tries a few fully automatic merging tools; if + these don't succeed (because the resolution process requires + human guidance) or aren't present, the script tries a few + different graphical merging tools.</para> + + <para>It's also possible to get Mercurial to run another program + or script instead of <command>hgmerge</command>, by setting the + <envar>HGMERGE</envar> environment variable to the name of your + preferred program.</para> + + <sect2> + <title>Using a graphical merge tool</title> + + <para>My preferred graphical merge tool is + <command>kdiff3</command>, which I'll use to describe the + features that are common to graphical file merging tools. You + can see a screenshot of <command>kdiff3</command> in action in + figure <xref endterm="fig.tour-merge.kdiff3.caption" + linkend="fig.tour-merge.kdiff3"/>. The kind of + merge it is performing is called a <emphasis>three-way + merge</emphasis>, because there are three different versions + of the file of interest to us. The tool thus splits the upper + portion of the window into three panes:</para> + <itemizedlist> + <listitem><para>At the left is the <emphasis>base</emphasis> + version of the file, i.e. the most recent version from + which the two versions we're trying to merge are + descended.</para> + </listitem> + <listitem><para>In the middle is <quote>our</quote> version of + the file, with the contents that we modified.</para> + </listitem> + <listitem><para>On the right is <quote>their</quote> version + of the file, the one that from the changeset that we're + trying to merge with.</para> + </listitem></itemizedlist> + <para>In the pane below these is the current + <emphasis>result</emphasis> of the merge. Our task is to + replace all of the red text, which indicates unresolved + conflicts, with some sensible merger of the + <quote>ours</quote> and <quote>theirs</quote> versions of the + file.</para> + + <para>All four of these panes are <emphasis>locked + together</emphasis>; if we scroll vertically or horizontally + in any of them, the others are updated to display the + corresponding sections of their respective files.</para> + + <informalfigure id="fig.tour-merge.kdiff3"> + <mediaobject> + <imageobject><imagedata width="100%" fileref="images/kdiff3.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.tour-merge.kdiff3.caption">Using + <command>kdiff3</command> to merge versions of a file</para> + </caption> + </mediaobject> + </informalfigure> + + <para>For each conflicting portion of the file, we can choose to + resolve the conflict using some combination of text from the + base version, ours, or theirs. We can also manually edit the + merged file at any time, in case we need to make further + modifications.</para> + + <para>There are <emphasis>many</emphasis> file merging tools + available, too many to cover here. They vary in which + platforms they are available for, and in their particular + strengths and weaknesses. Most are tuned for merging files + containing plain text, while a few are aimed at specialised + file formats (generally XML).</para> + + </sect2> + <sect2> + <title>A worked example</title> + + <para>In this example, we will reproduce the file modification + history of figure <xref endterm="fig.tour-merge.conflict.caption" + linkend="fig.tour-merge.conflict"/> + above. Let's begin by creating a repository with a base + version of our document.</para> + + &interaction.tour-merge-conflict.wife; + + <para>We'll clone the repository and make a change to the + file.</para> + + &interaction.tour-merge-conflict.cousin; + + <para>And another clone, to simulate someone else making a + change to the file. (This hints at the idea that it's not all + that unusual to merge with yourself when you isolate tasks in + separate repositories, and indeed to find and resolve + conflicts while doing so.)</para> + + &interaction.tour-merge-conflict.son; + + <para>Having created two + different versions of the file, we'll set up an environment + suitable for running our merge.</para> + + &interaction.tour-merge-conflict.pull; + + <para>In this example, I won't use Mercurial's normal + <command>hgmerge</command> program to do the merge, because it + would drop my nice automated example-running tool into a + graphical user interface. Instead, I'll set + <envar>HGMERGE</envar> to tell Mercurial to use the + non-interactive <command>merge</command> command. This is + bundled with many Unix-like systems. If you're following this + example on your computer, don't bother setting + <envar>HGMERGE</envar>.</para> + + <para><emphasis role="bold">XXX FIX THIS + EXAMPLE.</emphasis></para> + + &interaction.tour-merge-conflict.merge; + + <para>Because <command>merge</command> can't resolve the + conflicting changes, it leaves <emphasis>merge + markers</emphasis> inside the file that has conflicts, + indicating which lines have conflicts, and whether they came + from our version of the file or theirs.</para> + + <para>Mercurial can tell from the way <command>merge</command> + exits that it wasn't able to merge successfully, so it tells + us what commands we'll need to run if we want to redo the + merging operation. This could be useful if, for example, we + were running a graphical merge tool and quit because we were + confused or realised we had made a mistake.</para> + + <para>If automatic or manual merges fail, there's nothing to + prevent us from <quote>fixing up</quote> the affected files + ourselves, and committing the results of our merge:</para> + + &interaction.tour-merge-conflict.commit; + + </sect2> + </sect1> + <sect1 id="sec.tour-merge.fetch"> + <title>Simplifying the pull-merge-commit sequence</title> + + <para>The process of merging changes as outlined above is + straightforward, but requires running three commands in + sequence.</para> + <programlisting>hg pull +hg merge +hg commit -m 'Merged remote changes'</programlisting> + <para>In the case of the final commit, you also need to enter a + commit message, which is almost always going to be a piece of + uninteresting <quote>boilerplate</quote> text.</para> + + <para>It would be nice to reduce the number of steps needed, if + this were possible. Indeed, Mercurial is distributed with an + extension called <literal role="hg-ext">fetch</literal> that + does just this.</para> + + <para>Mercurial provides a flexible extension mechanism that lets + people extend its functionality, while keeping the core of + Mercurial small and easy to deal with. Some extensions add new + commands that you can use from the command line, while others + work <quote>behind the scenes,</quote> for example adding + capabilities to the server.</para> + + <para>The <literal role="hg-ext">fetch</literal> extension adds a + new command called, not surprisingly, <command role="hg-cmd">hg + fetch</command>. This extension acts as a combination of + <command role="hg-cmd">hg pull</command>, <command + role="hg-cmd">hg update</command> and <command + role="hg-cmd">hg merge</command>. It begins by pulling + changes from another repository into the current repository. If + it finds that the changes added a new head to the repository, it + begins a merge, then commits the result of the merge with an + automatically-generated commit message. If no new heads were + added, it updates the working directory to the new tip + changeset.</para> + + <para>Enabling the <literal role="hg-ext">fetch</literal> + extension is easy. Edit your <filename + role="special">.hgrc</filename>, and either go to the <literal + role="rc-extensions">extensions</literal> section or create an + <literal role="rc-extensions">extensions</literal> section. Then + add a line that simply reads <quote><literal>fetch + </literal></quote>.</para> + <programlisting>[extensions] +fetch =</programlisting> + <para>(Normally, on the right-hand side of the + <quote><literal>=</literal></quote> would appear the location of + the extension, but since the <literal + role="hg-ext">fetch</literal> extension is in the standard + distribution, Mercurial knows where to search for it.)</para> + + </sect1> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch03-concepts.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,751 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.concepts"> + <?dbhtml filename="behind-the-scenes.html"?> + <title>Behind the scenes</title> + + <para>Unlike many revision control systems, the concepts upon which + Mercurial is built are simple enough that it's easy to understand + how the software really works. Knowing this certainly isn't + necessary, but I find it useful to have a <quote>mental + model</quote> of what's going on.</para> + + <para>This understanding gives me confidence that Mercurial has been + carefully designed to be both <emphasis>safe</emphasis> and + <emphasis>efficient</emphasis>. And just as importantly, if it's + easy for me to retain a good idea of what the software is doing + when I perform a revision control task, I'm less likely to be + surprised by its behaviour.</para> + + <para>In this chapter, we'll initially cover the core concepts + behind Mercurial's design, then continue to discuss some of the + interesting details of its implementation.</para> + + <sect1> + <title>Mercurial's historical record</title> + + <sect2> + <title>Tracking the history of a single file</title> + + <para>When Mercurial tracks modifications to a file, it stores + the history of that file in a metadata object called a + <emphasis>filelog</emphasis>. Each entry in the filelog + contains enough information to reconstruct one revision of the + file that is being tracked. Filelogs are stored as files in + the <filename role="special" + class="directory">.hg/store/data</filename> directory. A + filelog contains two kinds of information: revision data, and + an index to help Mercurial to find a revision + efficiently.</para> + + <para>A file that is large, or has a lot of history, has its + filelog stored in separate data + (<quote><literal>.d</literal></quote> suffix) and index + (<quote><literal>.i</literal></quote> suffix) files. For + small files without much history, the revision data and index + are combined in a single <quote><literal>.i</literal></quote> + file. The correspondence between a file in the working + directory and the filelog that tracks its history in the + repository is illustrated in figure <xref + endterm="fig.concepts.filelog.caption" + linkend="fig.concepts.filelog"/>.</para> + + <informalfigure id="fig.concepts.filelog"> + <mediaobject> + <imageobject><imagedata fileref="images/filelog.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.filelog.caption">Relationships between + files in working directory and filelogs in repository</para> + </caption> + </mediaobject> + </informalfigure> + + </sect2> + <sect2> + <title>Managing tracked files</title> + + <para>Mercurial uses a structure called a + <emphasis>manifest</emphasis> to collect together information + about the files that it tracks. Each entry in the manifest + contains information about the files present in a single + changeset. An entry records which files are present in the + changeset, the revision of each file, and a few other pieces + of file metadata.</para> + + </sect2> + <sect2> + <title>Recording changeset information</title> + + <para>The <emphasis>changelog</emphasis> contains information + about each changeset. Each revision records who committed a + change, the changeset comment, other pieces of + changeset-related information, and the revision of the + manifest to use.</para> + + </sect2> + <sect2> + <title>Relationships between revisions</title> + + <para>Within a changelog, a manifest, or a filelog, each + revision stores a pointer to its immediate parent (or to its + two parents, if it's a merge revision). As I mentioned above, + there are also relationships between revisions + <emphasis>across</emphasis> these structures, and they are + hierarchical in nature.</para> + + <para>For every changeset in a repository, there is exactly one + revision stored in the changelog. Each revision of the + changelog contains a pointer to a single revision of the + manifest. A revision of the manifest stores a pointer to a + single revision of each filelog tracked when that changeset + was created. These relationships are illustrated in figure + <xref endterm="fig.concepts.metadata.caption" + linkend="fig.concepts.metadata"/>.</para> + + <informalfigure id="fig.concepts.metadata"> + <mediaobject> + <imageobject><imagedata fileref="images/metadata.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.metadata.caption">Metadata + relationships</para></caption> + </mediaobject> + </informalfigure> + + <para>As the illustration shows, there is + <emphasis>not</emphasis> a <quote>one to one</quote> + relationship between revisions in the changelog, manifest, or + filelog. If the manifest hasn't changed between two + changesets, the changelog entries for those changesets will + point to the same revision of the manifest. If a file that + Mercurial tracks hasn't changed between two changesets, the + entry for that file in the two revisions of the manifest will + point to the same revision of its filelog.</para> + + </sect2> + </sect1> + <sect1> + <title>Safe, efficient storage</title> + + <para>The underpinnings of changelogs, manifests, and filelogs are + provided by a single structure called the + <emphasis>revlog</emphasis>.</para> + + <sect2> + <title>Efficient storage</title> + + <para>The revlog provides efficient storage of revisions using a + <emphasis>delta</emphasis> mechanism. Instead of storing a + complete copy of a file for each revision, it stores the + changes needed to transform an older revision into the new + revision. For many kinds of file data, these deltas are + typically a fraction of a percent of the size of a full copy + of a file.</para> + + <para>Some obsolete revision control systems can only work with + deltas of text files. They must either store binary files as + complete snapshots or encoded into a text representation, both + of which are wasteful approaches. Mercurial can efficiently + handle deltas of files with arbitrary binary contents; it + doesn't need to treat text as special.</para> + + </sect2> + <sect2 id="sec.concepts.txn"> + <title>Safe operation</title> + + <para>Mercurial only ever <emphasis>appends</emphasis> data to + the end of a revlog file. It never modifies a section of a + file after it has written it. This is both more robust and + efficient than schemes that need to modify or rewrite + data.</para> + + <para>In addition, Mercurial treats every write as part of a + <emphasis>transaction</emphasis> that can span a number of + files. A transaction is <emphasis>atomic</emphasis>: either + the entire transaction succeeds and its effects are all + visible to readers in one go, or the whole thing is undone. + This guarantee of atomicity means that if you're running two + copies of Mercurial, where one is reading data and one is + writing it, the reader will never see a partially written + result that might confuse it.</para> + + <para>The fact that Mercurial only appends to files makes it + easier to provide this transactional guarantee. The easier it + is to do stuff like this, the more confident you should be + that it's done correctly.</para> + + </sect2> + <sect2> + <title>Fast retrieval</title> + + <para>Mercurial cleverly avoids a pitfall common to all earlier + revision control systems: the problem of <emphasis>inefficient + retrieval</emphasis>. Most revision control systems store + the contents of a revision as an incremental series of + modifications against a <quote>snapshot</quote>. To + reconstruct a specific revision, you must first read the + snapshot, and then every one of the revisions between the + snapshot and your target revision. The more history that a + file accumulates, the more revisions you must read, hence the + longer it takes to reconstruct a particular revision.</para> + + <informalfigure id="fig.concepts.snapshot"> + <mediaobject> + <imageobject><imagedata fileref="images/snapshot.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.snapshot.caption">Snapshot of + a revlog, with incremental deltas</para></caption> + </mediaobject> + </informalfigure> + + <para>The innovation that Mercurial applies to this problem is + simple but effective. Once the cumulative amount of delta + information stored since the last snapshot exceeds a fixed + threshold, it stores a new snapshot (compressed, of course), + instead of another delta. This makes it possible to + reconstruct <emphasis>any</emphasis> revision of a file + quickly. This approach works so well that it has since been + copied by several other revision control systems.</para> + + <para>Figure <xref endterm="fig.concepts.snapshot.caption" + linkend="fig.concepts.snapshot"/> illustrates + the idea. In an entry in a revlog's index file, Mercurial + stores the range of entries from the data file that it must + read to reconstruct a particular revision.</para> + + <sect3> + <title>Aside: the influence of video compression</title> + + <para>If you're familiar with video compression or have ever + watched a TV feed through a digital cable or satellite + service, you may know that most video compression schemes + store each frame of video as a delta against its predecessor + frame. In addition, these schemes use <quote>lossy</quote> + compression techniques to increase the compression ratio, so + visual errors accumulate over the course of a number of + inter-frame deltas.</para> + + <para>Because it's possible for a video stream to <quote>drop + out</quote> occasionally due to signal glitches, and to + limit the accumulation of artefacts introduced by the lossy + compression process, video encoders periodically insert a + complete frame (called a <quote>key frame</quote>) into the + video stream; the next delta is generated against that + frame. This means that if the video signal gets + interrupted, it will resume once the next key frame is + received. Also, the accumulation of encoding errors + restarts anew with each key frame.</para> + + </sect3> + </sect2> + <sect2> + <title>Identification and strong integrity</title> + + <para>Along with delta or snapshot information, a revlog entry + contains a cryptographic hash of the data that it represents. + This makes it difficult to forge the contents of a revision, + and easy to detect accidental corruption.</para> + + <para>Hashes provide more than a mere check against corruption; + they are used as the identifiers for revisions. The changeset + identification hashes that you see as an end user are from + revisions of the changelog. Although filelogs and the + manifest also use hashes, Mercurial only uses these behind the + scenes.</para> + + <para>Mercurial verifies that hashes are correct when it + retrieves file revisions and when it pulls changes from + another repository. If it encounters an integrity problem, it + will complain and stop whatever it's doing.</para> + + <para>In addition to the effect it has on retrieval efficiency, + Mercurial's use of periodic snapshots makes it more robust + against partial data corruption. If a revlog becomes partly + corrupted due to a hardware error or system bug, it's often + possible to reconstruct some or most revisions from the + uncorrupted sections of the revlog, both before and after the + corrupted section. This would not be possible with a + delta-only storage model.</para> + + </sect2> + </sect1> + <sect1> + <title>Revision history, branching, and merging</title> + + <para>Every entry in a Mercurial revlog knows the identity of its + immediate ancestor revision, usually referred to as its + <emphasis>parent</emphasis>. In fact, a revision contains room + for not one parent, but two. Mercurial uses a special hash, + called the <quote>null ID</quote>, to represent the idea + <quote>there is no parent here</quote>. This hash is simply a + string of zeroes.</para> + + <para>In figure <xref endterm="fig.concepts.revlog.caption" + linkend="fig.concepts.revlog"/>, you can see + an example of the conceptual structure of a revlog. Filelogs, + manifests, and changelogs all have this same structure; they + differ only in the kind of data stored in each delta or + snapshot.</para> + + <para>The first revision in a revlog (at the bottom of the image) + has the null ID in both of its parent slots. For a + <quote>normal</quote> revision, its first parent slot contains + the ID of its parent revision, and its second contains the null + ID, indicating that the revision has only one real parent. Any + two revisions that have the same parent ID are branches. A + revision that represents a merge between branches has two normal + revision IDs in its parent slots.</para> + + <informalfigure id="fig.concepts.revlog"> + <mediaobject> + <imageobject><imagedata fileref="images/revlog.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.revlog.caption">Revision in revlog</para> + </caption> + </mediaobject> + </informalfigure> + + </sect1> + <sect1> + <title>The working directory</title> + + <para>In the working directory, Mercurial stores a snapshot of the + files from the repository as of a particular changeset.</para> + + <para>The working directory <quote>knows</quote> which changeset + it contains. When you update the working directory to contain a + particular changeset, Mercurial looks up the appropriate + revision of the manifest to find out which files it was tracking + at the time that changeset was committed, and which revision of + each file was then current. It then recreates a copy of each of + those files, with the same contents it had when the changeset + was committed.</para> + + <para>The <emphasis>dirstate</emphasis> contains Mercurial's + knowledge of the working directory. This details which + changeset the working directory is updated to, and all of the + files that Mercurial is tracking in the working + directory.</para> + + <para>Just as a revision of a revlog has room for two parents, so + that it can represent either a normal revision (with one parent) + or a merge of two earlier revisions, the dirstate has slots for + two parents. When you use the <command role="hg-cmd">hg + update</command> command, the changeset that you update to is + stored in the <quote>first parent</quote> slot, and the null ID + in the second. When you <command role="hg-cmd">hg + merge</command> with another changeset, the first parent + remains unchanged, and the second parent is filled in with the + changeset you're merging with. The <command role="hg-cmd">hg + parents</command> command tells you what the parents of the + dirstate are.</para> + + <sect2> + <title>What happens when you commit</title> + + <para>The dirstate stores parent information for more than just + book-keeping purposes. Mercurial uses the parents of the + dirstate as <emphasis>the parents of a new + changeset</emphasis> when you perform a commit.</para> + + <informalfigure id="fig.concepts.wdir"> + <mediaobject> + <imageobject><imagedata fileref="images/wdir.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.wdir.caption">The working + directory can have two parents</para></caption> + </mediaobject> + </informalfigure> + + <para>Figure <xref endterm="fig.concepts.wdir.caption" + linkend="fig.concepts.wdir"/> shows the + normal state of the working directory, where it has a single + changeset as parent. That changeset is the + <emphasis>tip</emphasis>, the newest changeset in the + repository that has no children.</para> + + <informalfigure id="fig.concepts.wdir-after-commit"> + <mediaobject> + <imageobject><imagedata fileref="images/wdir-after-commit.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.wdir-after-commit.caption">The working + directory gains new parents after a commit</para></caption> + </mediaobject> + </informalfigure> + + <para>It's useful to think of the working directory as + <quote>the changeset I'm about to commit</quote>. Any files + that you tell Mercurial that you've added, removed, renamed, + or copied will be reflected in that changeset, as will + modifications to any files that Mercurial is already tracking; + the new changeset will have the parents of the working + directory as its parents.</para> + + <para>After a commit, Mercurial will update the parents of the + working directory, so that the first parent is the ID of the + new changeset, and the second is the null ID. This is shown + in figure <xref endterm="fig.concepts.wdir-after-commit.caption" + linkend="fig.concepts.wdir-after-commit"/>. + Mercurial + doesn't touch any of the files in the working directory when + you commit; it just modifies the dirstate to note its new + parents.</para> + + </sect2> + <sect2> + <title>Creating a new head</title> + + <para>It's perfectly normal to update the working directory to a + changeset other than the current tip. For example, you might + want to know what your project looked like last Tuesday, or + you could be looking through changesets to see which one + introduced a bug. In cases like this, the natural thing to do + is update the working directory to the changeset you're + interested in, and then examine the files in the working + directory directly to see their contents as they were when you + committed that changeset. The effect of this is shown in + figure <xref endterm="fig.concepts.wdir-pre-branch.caption" + linkend="fig.concepts.wdir-pre-branch"/>.</para> + + <informalfigure id="fig.concepts.wdir-pre-branch"> + <mediaobject> + <imageobject><imagedata fileref="images/wdir-pre-branch.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.wdir-pre-branch.caption">The working + directory, updated to an older changeset</para></caption> + </mediaobject> + </informalfigure> + + <para>Having updated the working directory to an older + changeset, what happens if you make some changes, and then + commit? Mercurial behaves in the same way as I outlined + above. The parents of the working directory become the + parents of the new changeset. This new changeset has no + children, so it becomes the new tip. And the repository now + contains two changesets that have no children; we call these + <emphasis>heads</emphasis>. You can see the structure that + this creates in figure <xref + endterm="fig.concepts.wdir-branch.caption" + linkend="fig.concepts.wdir-branch"/>.</para> + + <informalfigure id="fig.concepts.wdir-branch"> + <mediaobject> + <imageobject><imagedata fileref="images/wdir-branch.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.wdir-branch.caption">After a + commit made while synced to an older changeset</para></caption> + </mediaobject> + </informalfigure> + + <note> + <para> If you're new to Mercurial, you should keep in mind a + common <quote>error</quote>, which is to use the <command + role="hg-cmd">hg pull</command> command without any + options. By default, the <command role="hg-cmd">hg + pull</command> command <emphasis>does not</emphasis> + update the working directory, so you'll bring new changesets + into your repository, but the working directory will stay + synced at the same changeset as before the pull. If you + make some changes and commit afterwards, you'll thus create + a new head, because your working directory isn't synced to + whatever the current tip is.</para> + + <para> I put the word <quote>error</quote> in quotes because + all that you need to do to rectify this situation is + <command role="hg-cmd">hg merge</command>, then <command + role="hg-cmd">hg commit</command>. In other words, this + almost never has negative consequences; it just surprises + people. I'll discuss other ways to avoid this behaviour, + and why Mercurial behaves in this initially surprising way, + later on.</para> + </note> + + </sect2> + <sect2> + <title>Merging heads</title> + + <para>When you run the <command role="hg-cmd">hg merge</command> + command, Mercurial leaves the first parent of the working + directory unchanged, and sets the second parent to the + changeset you're merging with, as shown in figure <xref + endterm="fig.concepts.wdir-merge.caption" + linkend="fig.concepts.wdir-merge"/>.</para> + + <informalfigure id="fig.concepts.wdir-merge"> + <mediaobject> + <imageobject><imagedata fileref="images/wdir-merge.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.concepts.wdir-merge.caption">Merging two + heads</para></caption> + </mediaobject> + </informalfigure> + + <para>Mercurial also has to modify the working directory, to + merge the files managed in the two changesets. Simplified a + little, the merging process goes like this, for every file in + the manifests of both changesets.</para> + <itemizedlist> + <listitem><para>If neither changeset has modified a file, do + nothing with that file.</para> + </listitem> + <listitem><para>If one changeset has modified a file, and the + other hasn't, create the modified copy of the file in the + working directory.</para> + </listitem> + <listitem><para>If one changeset has removed a file, and the + other hasn't (or has also deleted it), delete the file + from the working directory.</para> + </listitem> + <listitem><para>If one changeset has removed a file, but the + other has modified the file, ask the user what to do: keep + the modified file, or remove it?</para> + </listitem> + <listitem><para>If both changesets have modified a file, + invoke an external merge program to choose the new + contents for the merged file. This may require input from + the user.</para> + </listitem> + <listitem><para>If one changeset has modified a file, and the + other has renamed or copied the file, make sure that the + changes follow the new name of the file.</para> + </listitem></itemizedlist> + <para>There are more details&emdash;merging has plenty of corner + cases&emdash;but these are the most common choices that are + involved in a merge. As you can see, most cases are + completely automatic, and indeed most merges finish + automatically, without requiring your input to resolve any + conflicts.</para> + + <para>When you're thinking about what happens when you commit + after a merge, once again the working directory is <quote>the + changeset I'm about to commit</quote>. After the <command + role="hg-cmd">hg merge</command> command completes, the + working directory has two parents; these will become the + parents of the new changeset.</para> + + <para>Mercurial lets you perform multiple merges, but you must + commit the results of each individual merge as you go. This + is necessary because Mercurial only tracks two parents for + both revisions and the working directory. While it would be + technically possible to merge multiple changesets at once, the + prospect of user confusion and making a terrible mess of a + merge immediately becomes overwhelming.</para> + + </sect2> + </sect1> + <sect1> + <title>Other interesting design features</title> + + <para>In the sections above, I've tried to highlight some of the + most important aspects of Mercurial's design, to illustrate that + it pays careful attention to reliability and performance. + However, the attention to detail doesn't stop there. There are + a number of other aspects of Mercurial's construction that I + personally find interesting. I'll detail a few of them here, + separate from the <quote>big ticket</quote> items above, so that + if you're interested, you can gain a better idea of the amount + of thinking that goes into a well-designed system.</para> + + <sect2> + <title>Clever compression</title> + + <para>When appropriate, Mercurial will store both snapshots and + deltas in compressed form. It does this by always + <emphasis>trying to</emphasis> compress a snapshot or delta, + but only storing the compressed version if it's smaller than + the uncompressed version.</para> + + <para>This means that Mercurial does <quote>the right + thing</quote> when storing a file whose native form is + compressed, such as a <literal>zip</literal> archive or a JPEG + image. When these types of files are compressed a second + time, the resulting file is usually bigger than the + once-compressed form, and so Mercurial will store the plain + <literal>zip</literal> or JPEG.</para> + + <para>Deltas between revisions of a compressed file are usually + larger than snapshots of the file, and Mercurial again does + <quote>the right thing</quote> in these cases. It finds that + such a delta exceeds the threshold at which it should store a + complete snapshot of the file, so it stores the snapshot, + again saving space compared to a naive delta-only + approach.</para> + + <sect3> + <title>Network recompression</title> + + <para>When storing revisions on disk, Mercurial uses the + <quote>deflate</quote> compression algorithm (the same one + used by the popular <literal>zip</literal> archive format), + which balances good speed with a respectable compression + ratio. However, when transmitting revision data over a + network connection, Mercurial uncompresses the compressed + revision data.</para> + + <para>If the connection is over HTTP, Mercurial recompresses + the entire stream of data using a compression algorithm that + gives a better compression ratio (the Burrows-Wheeler + algorithm from the widely used <literal>bzip2</literal> + compression package). This combination of algorithm and + compression of the entire stream (instead of a revision at a + time) substantially reduces the number of bytes to be + transferred, yielding better network performance over almost + all kinds of network.</para> + + <para>(If the connection is over <command>ssh</command>, + Mercurial <emphasis>doesn't</emphasis> recompress the + stream, because <command>ssh</command> can already do this + itself.)</para> + + </sect3> + </sect2> + <sect2> + <title>Read/write ordering and atomicity</title> + + <para>Appending to files isn't the whole story when it comes to + guaranteeing that a reader won't see a partial write. If you + recall figure <xref endterm="fig.concepts.metadata.caption" + linkend="fig.concepts.metadata"/>, revisions in the + changelog point to revisions in the manifest, and revisions in + the manifest point to revisions in filelogs. This hierarchy + is deliberate.</para> + + <para>A writer starts a transaction by writing filelog and + manifest data, and doesn't write any changelog data until + those are finished. A reader starts by reading changelog + data, then manifest data, followed by filelog data.</para> + + <para>Since the writer has always finished writing filelog and + manifest data before it writes to the changelog, a reader will + never read a pointer to a partially written manifest revision + from the changelog, and it will never read a pointer to a + partially written filelog revision from the manifest.</para> + + </sect2> + <sect2> + <title>Concurrent access</title> + + <para>The read/write ordering and atomicity guarantees mean that + Mercurial never needs to <emphasis>lock</emphasis> a + repository when it's reading data, even if the repository is + being written to while the read is occurring. This has a big + effect on scalability; you can have an arbitrary number of + Mercurial processes safely reading data from a repository + safely all at once, no matter whether it's being written to or + not.</para> + + <para>The lockless nature of reading means that if you're + sharing a repository on a multi-user system, you don't need to + grant other local users permission to + <emphasis>write</emphasis> to your repository in order for + them to be able to clone it or pull changes from it; they only + need <emphasis>read</emphasis> permission. (This is + <emphasis>not</emphasis> a common feature among revision + control systems, so don't take it for granted! Most require + readers to be able to lock a repository to access it safely, + and this requires write permission on at least one directory, + which of course makes for all kinds of nasty and annoying + security and administrative problems.)</para> + + <para>Mercurial uses locks to ensure that only one process can + write to a repository at a time (the locking mechanism is safe + even over filesystems that are notoriously hostile to locking, + such as NFS). If a repository is locked, a writer will wait + for a while to retry if the repository becomes unlocked, but + if the repository remains locked for too long, the process + attempting to write will time out after a while. This means + that your daily automated scripts won't get stuck forever and + pile up if a system crashes unnoticed, for example. (Yes, the + timeout is configurable, from zero to infinity.)</para> + + <sect3> + <title>Safe dirstate access</title> + + <para>As with revision data, Mercurial doesn't take a lock to + read the dirstate file; it does acquire a lock to write it. + To avoid the possibility of reading a partially written copy + of the dirstate file, Mercurial writes to a file with a + unique name in the same directory as the dirstate file, then + renames the temporary file atomically to + <filename>dirstate</filename>. The file named + <filename>dirstate</filename> is thus guaranteed to be + complete, not partially written.</para> + + </sect3> + </sect2> + <sect2> + <title>Avoiding seeks</title> + + <para>Critical to Mercurial's performance is the avoidance of + seeks of the disk head, since any seek is far more expensive + than even a comparatively large read operation.</para> + + <para>This is why, for example, the dirstate is stored in a + single file. If there were a dirstate file per directory that + Mercurial tracked, the disk would seek once per directory. + Instead, Mercurial reads the entire single dirstate file in + one step.</para> + + <para>Mercurial also uses a <quote>copy on write</quote> scheme + when cloning a repository on local storage. Instead of + copying every revlog file from the old repository into the new + repository, it makes a <quote>hard link</quote>, which is a + shorthand way to say <quote>these two names point to the same + file</quote>. When Mercurial is about to write to one of a + revlog's files, it checks to see if the number of names + pointing at the file is greater than one. If it is, more than + one repository is using the file, so Mercurial makes a new + copy of the file that is private to this repository.</para> + + <para>A few revision control developers have pointed out that + this idea of making a complete private copy of a file is not + very efficient in its use of storage. While this is true, + storage is cheap, and this method gives the highest + performance while deferring most book-keeping to the operating + system. An alternative scheme would most likely reduce + performance and increase the complexity of the software, each + of which is much more important to the <quote>feel</quote> of + day-to-day use.</para> + + </sect2> + <sect2> + <title>Other contents of the dirstate</title> + + <para>Because Mercurial doesn't force you to tell it when you're + modifying a file, it uses the dirstate to store some extra + information so it can determine efficiently whether you have + modified a file. For each file in the working directory, it + stores the time that it last modified the file itself, and the + size of the file at that time.</para> + + <para>When you explicitly <command role="hg-cmd">hg + add</command>, <command role="hg-cmd">hg remove</command>, + <command role="hg-cmd">hg rename</command> or <command + role="hg-cmd">hg copy</command> files, Mercurial updates the + dirstate so that it knows what to do with those files when you + commit.</para> + + <para>When Mercurial is checking the states of files in the + working directory, it first checks a file's modification time. + If that has not changed, the file must not have been modified. + If the file's size has changed, the file must have been + modified. If the modification time has changed, but the size + has not, only then does Mercurial need to read the actual + contents of the file to see if they've changed. Storing these + few extra pieces of information dramatically reduces the + amount of data that Mercurial needs to read, which yields + large performance improvements compared to other revision + control systems.</para> + + </sect2> + </sect1> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- a/en/ch03-tour-merge.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,401 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.tour-merge"> - <?dbhtml filename="a-tour-of-mercurial-merging-work.html"?> - <title>A tour of Mercurial: merging work</title> - - <para>We've now covered cloning a repository, making changes in a - repository, and pulling or pushing changes from one repository - into another. Our next step is <emphasis>merging</emphasis> - changes from separate repositories.</para> - - <sect1> - <title>Merging streams of work</title> - - <para>Merging is a fundamental part of working with a distributed - revision control tool.</para> - <itemizedlist> - <listitem><para>Alice and Bob each have a personal copy of a - repository for a project they're collaborating on. Alice - fixes a bug in her repository; Bob adds a new feature in - his. They want the shared repository to contain both the - bug fix and the new feature.</para> - </listitem> - <listitem><para>I frequently work on several different tasks for - a single project at once, each safely isolated in its own - repository. Working this way means that I often need to - merge one piece of my own work with another.</para> - </listitem></itemizedlist> - - <para>Because merging is such a common thing to need to do, - Mercurial makes it easy. Let's walk through the process. We'll - begin by cloning yet another repository (see how often they - spring up?) and making a change in it.</para> - - &interaction.tour.merge.clone; - - <para>We should now have two copies of - <filename>hello.c</filename> with different contents. The - histories of the two repositories have also diverged, as - illustrated in figure <xref endterm="fig.tour-merge.sep-repos.caption" - linkend="fig.tour-merge.sep-repos"/>.</para> - - &interaction.tour.merge.cat; - - <informalfigure id="fig.tour-merge.sep-repos"> - <mediaobject> - <imageobject><imagedata fileref="images/tour-merge-sep-repos.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.tour-merge.sep-repos.caption">Divergent recent - histories of the <filename - class="directory">my-hello</filename> and <filename - class="directory">my-new-hello</filename> - repositories</para></caption> - </mediaobject> - </informalfigure> - - <para>We already know that pulling changes from our <filename - class="directory">my-hello</filename> repository will have no - effect on the working directory.</para> - - &interaction.tour.merge.pull; - - <para>However, the <command role="hg-cmd">hg pull</command> - command says something about <quote>heads</quote>.</para> - - <sect2> - <title>Head changesets</title> - - <para>A head is a change that has no descendants, or children, - as they're also known. The tip revision is thus a head, - because the newest revision in a repository doesn't have any - children, but a repository can contain more than one - head.</para> - - <informalfigure id="fig.tour-merge.pull"> - <mediaobject> - <imageobject><imagedata fileref="images/tour-merge-pull.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.tour-merge.pull.caption">Repository contents after - pulling from <filename class="directory">my-hello</filename> into - <filename class="directory">my-new-hello</filename></para></caption> - </mediaobject> - </informalfigure> - - <para>In figure <xref endterm="fig.tour-merge.pull.caption" - linkend="fig.tour-merge.pull"/>, you can - see the effect of the pull from <filename - class="directory">my-hello</filename> into <filename - class="directory">my-new-hello</filename>. The history that - was already present in <filename - class="directory">my-new-hello</filename> is untouched, but - a new revision has been added. By referring to figure <xref - endterm="fig.tour-merge.sep-repos.caption" - linkend="fig.tour-merge.sep-repos"/>, we can see that the - <emphasis>changeset ID</emphasis> remains the same in the new - repository, but the <emphasis>revision number</emphasis> has - changed. (This, incidentally, is a fine example of why it's - not safe to use revision numbers when discussing changesets.) - We can view the heads in a repository using the <command - role="hg-cmd">hg heads</command> command.</para> - - &interaction.tour.merge.heads; - - </sect2> - <sect2> - <title>Performing the merge</title> - - <para>What happens if we try to use the normal <command - role="hg-cmd">hg update</command> command to update to the - new tip?</para> - - &interaction.tour.merge.update; - - <para>Mercurial is telling us that the <command role="hg-cmd">hg - update</command> command won't do a merge; it won't update - the working directory when it thinks we might be wanting to do - a merge, unless we force it to do so. Instead, we use the - <command role="hg-cmd">hg merge</command> command to merge the - two heads.</para> - - &interaction.tour.merge.merge; - - <informalfigure id="fig.tour-merge.merge"> - <mediaobject> - <imageobject><imagedata fileref="images/tour-merge-merge.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.tour-merge.merge.caption">Working directory and - repository during merge, and following commit</para></caption> - </mediaobject> - </informalfigure> - - <para>This updates the working directory so that it contains - changes from <emphasis>both</emphasis> heads, which is - reflected in both the output of <command role="hg-cmd">hg - parents</command> and the contents of - <filename>hello.c</filename>.</para> - - &interaction.tour.merge.parents; - - </sect2> - <sect2> - <title>Committing the results of the merge</title> - - <para>Whenever we've done a merge, <command role="hg-cmd">hg - parents</command> will display two parents until we <command - role="hg-cmd">hg commit</command> the results of the - merge.</para> - - &interaction.tour.merge.commit; - - <para>We now have a new tip revision; notice that it has - <emphasis>both</emphasis> of our former heads as its parents. - These are the same revisions that were previously displayed by - <command role="hg-cmd">hg parents</command>.</para> - - &interaction.tour.merge.tip; - - <para>In figure <xref endterm="fig.tour-merge.merge.caption" - linkend="fig.tour-merge.merge"/>, you can see a - representation of what happens to the working directory during - the merge, and how this affects the repository when the commit - happens. During the merge, the working directory has two - parent changesets, and these become the parents of the new - changeset.</para> - - </sect2> - </sect1> - <sect1> - <title>Merging conflicting changes</title> - - <para>Most merges are simple affairs, but sometimes you'll find - yourself merging changes where each modifies the same portions - of the same files. Unless both modifications are identical, - this results in a <emphasis>conflict</emphasis>, where you have - to decide how to reconcile the different changes into something - coherent.</para> - - <informalfigure id="fig.tour-merge.conflict"> - <mediaobject> - <imageobject><imagedata fileref="images/tour-merge-conflict.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.tour-merge.conflict.caption">Conflicting - changes to a document</para></caption> - </mediaobject> - </informalfigure> - - <para>Figure <xref endterm="fig.tour-merge.conflict.caption" - linkend="fig.tour-merge.conflict"/> illustrates - an instance of two conflicting changes to a document. We - started with a single version of the file; then we made some - changes; while someone else made different changes to the same - text. Our task in resolving the conflicting changes is to - decide what the file should look like.</para> - - <para>Mercurial doesn't have a built-in facility for handling - conflicts. Instead, it runs an external program called - <command>hgmerge</command>. This is a shell script that is - bundled with Mercurial; you can change it to behave however you - please. What it does by default is try to find one of several - different merging tools that are likely to be installed on your - system. It first tries a few fully automatic merging tools; if - these don't succeed (because the resolution process requires - human guidance) or aren't present, the script tries a few - different graphical merging tools.</para> - - <para>It's also possible to get Mercurial to run another program - or script instead of <command>hgmerge</command>, by setting the - <envar>HGMERGE</envar> environment variable to the name of your - preferred program.</para> - - <sect2> - <title>Using a graphical merge tool</title> - - <para>My preferred graphical merge tool is - <command>kdiff3</command>, which I'll use to describe the - features that are common to graphical file merging tools. You - can see a screenshot of <command>kdiff3</command> in action in - figure <xref endterm="fig.tour-merge.kdiff3.caption" - linkend="fig.tour-merge.kdiff3"/>. The kind of - merge it is performing is called a <emphasis>three-way - merge</emphasis>, because there are three different versions - of the file of interest to us. The tool thus splits the upper - portion of the window into three panes:</para> - <itemizedlist> - <listitem><para>At the left is the <emphasis>base</emphasis> - version of the file, i.e. the most recent version from - which the two versions we're trying to merge are - descended.</para> - </listitem> - <listitem><para>In the middle is <quote>our</quote> version of - the file, with the contents that we modified.</para> - </listitem> - <listitem><para>On the right is <quote>their</quote> version - of the file, the one that from the changeset that we're - trying to merge with.</para> - </listitem></itemizedlist> - <para>In the pane below these is the current - <emphasis>result</emphasis> of the merge. Our task is to - replace all of the red text, which indicates unresolved - conflicts, with some sensible merger of the - <quote>ours</quote> and <quote>theirs</quote> versions of the - file.</para> - - <para>All four of these panes are <emphasis>locked - together</emphasis>; if we scroll vertically or horizontally - in any of them, the others are updated to display the - corresponding sections of their respective files.</para> - - <informalfigure id="fig.tour-merge.kdiff3"> - <mediaobject> - <imageobject><imagedata width="100%" fileref="images/kdiff3.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.tour-merge.kdiff3.caption">Using - <command>kdiff3</command> to merge versions of a file</para> - </caption> - </mediaobject> - </informalfigure> - - <para>For each conflicting portion of the file, we can choose to - resolve the conflict using some combination of text from the - base version, ours, or theirs. We can also manually edit the - merged file at any time, in case we need to make further - modifications.</para> - - <para>There are <emphasis>many</emphasis> file merging tools - available, too many to cover here. They vary in which - platforms they are available for, and in their particular - strengths and weaknesses. Most are tuned for merging files - containing plain text, while a few are aimed at specialised - file formats (generally XML).</para> - - </sect2> - <sect2> - <title>A worked example</title> - - <para>In this example, we will reproduce the file modification - history of figure <xref endterm="fig.tour-merge.conflict.caption" - linkend="fig.tour-merge.conflict"/> - above. Let's begin by creating a repository with a base - version of our document.</para> - - &interaction.tour-merge-conflict.wife; - - <para>We'll clone the repository and make a change to the - file.</para> - - &interaction.tour-merge-conflict.cousin; - - <para>And another clone, to simulate someone else making a - change to the file. (This hints at the idea that it's not all - that unusual to merge with yourself when you isolate tasks in - separate repositories, and indeed to find and resolve - conflicts while doing so.)</para> - - &interaction.tour-merge-conflict.son; - - <para>Having created two - different versions of the file, we'll set up an environment - suitable for running our merge.</para> - - &interaction.tour-merge-conflict.pull; - - <para>In this example, I won't use Mercurial's normal - <command>hgmerge</command> program to do the merge, because it - would drop my nice automated example-running tool into a - graphical user interface. Instead, I'll set - <envar>HGMERGE</envar> to tell Mercurial to use the - non-interactive <command>merge</command> command. This is - bundled with many Unix-like systems. If you're following this - example on your computer, don't bother setting - <envar>HGMERGE</envar>.</para> - - <para><emphasis role="bold">XXX FIX THIS - EXAMPLE.</emphasis></para> - - &interaction.tour-merge-conflict.merge; - - <para>Because <command>merge</command> can't resolve the - conflicting changes, it leaves <emphasis>merge - markers</emphasis> inside the file that has conflicts, - indicating which lines have conflicts, and whether they came - from our version of the file or theirs.</para> - - <para>Mercurial can tell from the way <command>merge</command> - exits that it wasn't able to merge successfully, so it tells - us what commands we'll need to run if we want to redo the - merging operation. This could be useful if, for example, we - were running a graphical merge tool and quit because we were - confused or realised we had made a mistake.</para> - - <para>If automatic or manual merges fail, there's nothing to - prevent us from <quote>fixing up</quote> the affected files - ourselves, and committing the results of our merge:</para> - - &interaction.tour-merge-conflict.commit; - - </sect2> - </sect1> - <sect1 id="sec.tour-merge.fetch"> - <title>Simplifying the pull-merge-commit sequence</title> - - <para>The process of merging changes as outlined above is - straightforward, but requires running three commands in - sequence.</para> - <programlisting>hg pull -hg merge -hg commit -m 'Merged remote changes'</programlisting> - <para>In the case of the final commit, you also need to enter a - commit message, which is almost always going to be a piece of - uninteresting <quote>boilerplate</quote> text.</para> - - <para>It would be nice to reduce the number of steps needed, if - this were possible. Indeed, Mercurial is distributed with an - extension called <literal role="hg-ext">fetch</literal> that - does just this.</para> - - <para>Mercurial provides a flexible extension mechanism that lets - people extend its functionality, while keeping the core of - Mercurial small and easy to deal with. Some extensions add new - commands that you can use from the command line, while others - work <quote>behind the scenes,</quote> for example adding - capabilities to the server.</para> - - <para>The <literal role="hg-ext">fetch</literal> extension adds a - new command called, not surprisingly, <command role="hg-cmd">hg - fetch</command>. This extension acts as a combination of - <command role="hg-cmd">hg pull</command>, <command - role="hg-cmd">hg update</command> and <command - role="hg-cmd">hg merge</command>. It begins by pulling - changes from another repository into the current repository. If - it finds that the changes added a new head to the repository, it - begins a merge, then commits the result of the merge with an - automatically-generated commit message. If no new heads were - added, it updates the working directory to the new tip - changeset.</para> - - <para>Enabling the <literal role="hg-ext">fetch</literal> - extension is easy. Edit your <filename - role="special">.hgrc</filename>, and either go to the <literal - role="rc-extensions">extensions</literal> section or create an - <literal role="rc-extensions">extensions</literal> section. Then - add a line that simply reads <quote><literal>fetch - </literal></quote>.</para> - <programlisting>[extensions] -fetch =</programlisting> - <para>(Normally, on the right-hand side of the - <quote><literal>=</literal></quote> would appear the location of - the extension, but since the <literal - role="hg-ext">fetch</literal> extension is in the standard - distribution, Mercurial knows where to search for it.)</para> - - </sect1> -</chapter> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- a/en/ch04-concepts.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,751 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.concepts"> - <?dbhtml filename="behind-the-scenes.html"?> - <title>Behind the scenes</title> - - <para>Unlike many revision control systems, the concepts upon which - Mercurial is built are simple enough that it's easy to understand - how the software really works. Knowing this certainly isn't - necessary, but I find it useful to have a <quote>mental - model</quote> of what's going on.</para> - - <para>This understanding gives me confidence that Mercurial has been - carefully designed to be both <emphasis>safe</emphasis> and - <emphasis>efficient</emphasis>. And just as importantly, if it's - easy for me to retain a good idea of what the software is doing - when I perform a revision control task, I'm less likely to be - surprised by its behaviour.</para> - - <para>In this chapter, we'll initially cover the core concepts - behind Mercurial's design, then continue to discuss some of the - interesting details of its implementation.</para> - - <sect1> - <title>Mercurial's historical record</title> - - <sect2> - <title>Tracking the history of a single file</title> - - <para>When Mercurial tracks modifications to a file, it stores - the history of that file in a metadata object called a - <emphasis>filelog</emphasis>. Each entry in the filelog - contains enough information to reconstruct one revision of the - file that is being tracked. Filelogs are stored as files in - the <filename role="special" - class="directory">.hg/store/data</filename> directory. A - filelog contains two kinds of information: revision data, and - an index to help Mercurial to find a revision - efficiently.</para> - - <para>A file that is large, or has a lot of history, has its - filelog stored in separate data - (<quote><literal>.d</literal></quote> suffix) and index - (<quote><literal>.i</literal></quote> suffix) files. For - small files without much history, the revision data and index - are combined in a single <quote><literal>.i</literal></quote> - file. The correspondence between a file in the working - directory and the filelog that tracks its history in the - repository is illustrated in figure <xref - endterm="fig.concepts.filelog.caption" - linkend="fig.concepts.filelog"/>.</para> - - <informalfigure id="fig.concepts.filelog"> - <mediaobject> - <imageobject><imagedata fileref="images/filelog.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.filelog.caption">Relationships between - files in working directory and filelogs in repository</para> - </caption> - </mediaobject> - </informalfigure> - - </sect2> - <sect2> - <title>Managing tracked files</title> - - <para>Mercurial uses a structure called a - <emphasis>manifest</emphasis> to collect together information - about the files that it tracks. Each entry in the manifest - contains information about the files present in a single - changeset. An entry records which files are present in the - changeset, the revision of each file, and a few other pieces - of file metadata.</para> - - </sect2> - <sect2> - <title>Recording changeset information</title> - - <para>The <emphasis>changelog</emphasis> contains information - about each changeset. Each revision records who committed a - change, the changeset comment, other pieces of - changeset-related information, and the revision of the - manifest to use.</para> - - </sect2> - <sect2> - <title>Relationships between revisions</title> - - <para>Within a changelog, a manifest, or a filelog, each - revision stores a pointer to its immediate parent (or to its - two parents, if it's a merge revision). As I mentioned above, - there are also relationships between revisions - <emphasis>across</emphasis> these structures, and they are - hierarchical in nature.</para> - - <para>For every changeset in a repository, there is exactly one - revision stored in the changelog. Each revision of the - changelog contains a pointer to a single revision of the - manifest. A revision of the manifest stores a pointer to a - single revision of each filelog tracked when that changeset - was created. These relationships are illustrated in figure - <xref endterm="fig.concepts.metadata.caption" - linkend="fig.concepts.metadata"/>.</para> - - <informalfigure id="fig.concepts.metadata"> - <mediaobject> - <imageobject><imagedata fileref="images/metadata.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.metadata.caption">Metadata - relationships</para></caption> - </mediaobject> - </informalfigure> - - <para>As the illustration shows, there is - <emphasis>not</emphasis> a <quote>one to one</quote> - relationship between revisions in the changelog, manifest, or - filelog. If the manifest hasn't changed between two - changesets, the changelog entries for those changesets will - point to the same revision of the manifest. If a file that - Mercurial tracks hasn't changed between two changesets, the - entry for that file in the two revisions of the manifest will - point to the same revision of its filelog.</para> - - </sect2> - </sect1> - <sect1> - <title>Safe, efficient storage</title> - - <para>The underpinnings of changelogs, manifests, and filelogs are - provided by a single structure called the - <emphasis>revlog</emphasis>.</para> - - <sect2> - <title>Efficient storage</title> - - <para>The revlog provides efficient storage of revisions using a - <emphasis>delta</emphasis> mechanism. Instead of storing a - complete copy of a file for each revision, it stores the - changes needed to transform an older revision into the new - revision. For many kinds of file data, these deltas are - typically a fraction of a percent of the size of a full copy - of a file.</para> - - <para>Some obsolete revision control systems can only work with - deltas of text files. They must either store binary files as - complete snapshots or encoded into a text representation, both - of which are wasteful approaches. Mercurial can efficiently - handle deltas of files with arbitrary binary contents; it - doesn't need to treat text as special.</para> - - </sect2> - <sect2 id="sec.concepts.txn"> - <title>Safe operation</title> - - <para>Mercurial only ever <emphasis>appends</emphasis> data to - the end of a revlog file. It never modifies a section of a - file after it has written it. This is both more robust and - efficient than schemes that need to modify or rewrite - data.</para> - - <para>In addition, Mercurial treats every write as part of a - <emphasis>transaction</emphasis> that can span a number of - files. A transaction is <emphasis>atomic</emphasis>: either - the entire transaction succeeds and its effects are all - visible to readers in one go, or the whole thing is undone. - This guarantee of atomicity means that if you're running two - copies of Mercurial, where one is reading data and one is - writing it, the reader will never see a partially written - result that might confuse it.</para> - - <para>The fact that Mercurial only appends to files makes it - easier to provide this transactional guarantee. The easier it - is to do stuff like this, the more confident you should be - that it's done correctly.</para> - - </sect2> - <sect2> - <title>Fast retrieval</title> - - <para>Mercurial cleverly avoids a pitfall common to all earlier - revision control systems: the problem of <emphasis>inefficient - retrieval</emphasis>. Most revision control systems store - the contents of a revision as an incremental series of - modifications against a <quote>snapshot</quote>. To - reconstruct a specific revision, you must first read the - snapshot, and then every one of the revisions between the - snapshot and your target revision. The more history that a - file accumulates, the more revisions you must read, hence the - longer it takes to reconstruct a particular revision.</para> - - <informalfigure id="fig.concepts.snapshot"> - <mediaobject> - <imageobject><imagedata fileref="images/snapshot.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.snapshot.caption">Snapshot of - a revlog, with incremental deltas</para></caption> - </mediaobject> - </informalfigure> - - <para>The innovation that Mercurial applies to this problem is - simple but effective. Once the cumulative amount of delta - information stored since the last snapshot exceeds a fixed - threshold, it stores a new snapshot (compressed, of course), - instead of another delta. This makes it possible to - reconstruct <emphasis>any</emphasis> revision of a file - quickly. This approach works so well that it has since been - copied by several other revision control systems.</para> - - <para>Figure <xref endterm="fig.concepts.snapshot.caption" - linkend="fig.concepts.snapshot"/> illustrates - the idea. In an entry in a revlog's index file, Mercurial - stores the range of entries from the data file that it must - read to reconstruct a particular revision.</para> - - <sect3> - <title>Aside: the influence of video compression</title> - - <para>If you're familiar with video compression or have ever - watched a TV feed through a digital cable or satellite - service, you may know that most video compression schemes - store each frame of video as a delta against its predecessor - frame. In addition, these schemes use <quote>lossy</quote> - compression techniques to increase the compression ratio, so - visual errors accumulate over the course of a number of - inter-frame deltas.</para> - - <para>Because it's possible for a video stream to <quote>drop - out</quote> occasionally due to signal glitches, and to - limit the accumulation of artefacts introduced by the lossy - compression process, video encoders periodically insert a - complete frame (called a <quote>key frame</quote>) into the - video stream; the next delta is generated against that - frame. This means that if the video signal gets - interrupted, it will resume once the next key frame is - received. Also, the accumulation of encoding errors - restarts anew with each key frame.</para> - - </sect3> - </sect2> - <sect2> - <title>Identification and strong integrity</title> - - <para>Along with delta or snapshot information, a revlog entry - contains a cryptographic hash of the data that it represents. - This makes it difficult to forge the contents of a revision, - and easy to detect accidental corruption.</para> - - <para>Hashes provide more than a mere check against corruption; - they are used as the identifiers for revisions. The changeset - identification hashes that you see as an end user are from - revisions of the changelog. Although filelogs and the - manifest also use hashes, Mercurial only uses these behind the - scenes.</para> - - <para>Mercurial verifies that hashes are correct when it - retrieves file revisions and when it pulls changes from - another repository. If it encounters an integrity problem, it - will complain and stop whatever it's doing.</para> - - <para>In addition to the effect it has on retrieval efficiency, - Mercurial's use of periodic snapshots makes it more robust - against partial data corruption. If a revlog becomes partly - corrupted due to a hardware error or system bug, it's often - possible to reconstruct some or most revisions from the - uncorrupted sections of the revlog, both before and after the - corrupted section. This would not be possible with a - delta-only storage model.</para> - - </sect2> - </sect1> - <sect1> - <title>Revision history, branching, and merging</title> - - <para>Every entry in a Mercurial revlog knows the identity of its - immediate ancestor revision, usually referred to as its - <emphasis>parent</emphasis>. In fact, a revision contains room - for not one parent, but two. Mercurial uses a special hash, - called the <quote>null ID</quote>, to represent the idea - <quote>there is no parent here</quote>. This hash is simply a - string of zeroes.</para> - - <para>In figure <xref endterm="fig.concepts.revlog.caption" - linkend="fig.concepts.revlog"/>, you can see - an example of the conceptual structure of a revlog. Filelogs, - manifests, and changelogs all have this same structure; they - differ only in the kind of data stored in each delta or - snapshot.</para> - - <para>The first revision in a revlog (at the bottom of the image) - has the null ID in both of its parent slots. For a - <quote>normal</quote> revision, its first parent slot contains - the ID of its parent revision, and its second contains the null - ID, indicating that the revision has only one real parent. Any - two revisions that have the same parent ID are branches. A - revision that represents a merge between branches has two normal - revision IDs in its parent slots.</para> - - <informalfigure id="fig.concepts.revlog"> - <mediaobject> - <imageobject><imagedata fileref="images/revlog.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.revlog.caption">Revision in revlog</para> - </caption> - </mediaobject> - </informalfigure> - - </sect1> - <sect1> - <title>The working directory</title> - - <para>In the working directory, Mercurial stores a snapshot of the - files from the repository as of a particular changeset.</para> - - <para>The working directory <quote>knows</quote> which changeset - it contains. When you update the working directory to contain a - particular changeset, Mercurial looks up the appropriate - revision of the manifest to find out which files it was tracking - at the time that changeset was committed, and which revision of - each file was then current. It then recreates a copy of each of - those files, with the same contents it had when the changeset - was committed.</para> - - <para>The <emphasis>dirstate</emphasis> contains Mercurial's - knowledge of the working directory. This details which - changeset the working directory is updated to, and all of the - files that Mercurial is tracking in the working - directory.</para> - - <para>Just as a revision of a revlog has room for two parents, so - that it can represent either a normal revision (with one parent) - or a merge of two earlier revisions, the dirstate has slots for - two parents. When you use the <command role="hg-cmd">hg - update</command> command, the changeset that you update to is - stored in the <quote>first parent</quote> slot, and the null ID - in the second. When you <command role="hg-cmd">hg - merge</command> with another changeset, the first parent - remains unchanged, and the second parent is filled in with the - changeset you're merging with. The <command role="hg-cmd">hg - parents</command> command tells you what the parents of the - dirstate are.</para> - - <sect2> - <title>What happens when you commit</title> - - <para>The dirstate stores parent information for more than just - book-keeping purposes. Mercurial uses the parents of the - dirstate as <emphasis>the parents of a new - changeset</emphasis> when you perform a commit.</para> - - <informalfigure id="fig.concepts.wdir"> - <mediaobject> - <imageobject><imagedata fileref="images/wdir.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.wdir.caption">The working - directory can have two parents</para></caption> - </mediaobject> - </informalfigure> - - <para>Figure <xref endterm="fig.concepts.wdir.caption" - linkend="fig.concepts.wdir"/> shows the - normal state of the working directory, where it has a single - changeset as parent. That changeset is the - <emphasis>tip</emphasis>, the newest changeset in the - repository that has no children.</para> - - <informalfigure id="fig.concepts.wdir-after-commit"> - <mediaobject> - <imageobject><imagedata fileref="images/wdir-after-commit.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.wdir-after-commit.caption">The working - directory gains new parents after a commit</para></caption> - </mediaobject> - </informalfigure> - - <para>It's useful to think of the working directory as - <quote>the changeset I'm about to commit</quote>. Any files - that you tell Mercurial that you've added, removed, renamed, - or copied will be reflected in that changeset, as will - modifications to any files that Mercurial is already tracking; - the new changeset will have the parents of the working - directory as its parents.</para> - - <para>After a commit, Mercurial will update the parents of the - working directory, so that the first parent is the ID of the - new changeset, and the second is the null ID. This is shown - in figure <xref endterm="fig.concepts.wdir-after-commit.caption" - linkend="fig.concepts.wdir-after-commit"/>. - Mercurial - doesn't touch any of the files in the working directory when - you commit; it just modifies the dirstate to note its new - parents.</para> - - </sect2> - <sect2> - <title>Creating a new head</title> - - <para>It's perfectly normal to update the working directory to a - changeset other than the current tip. For example, you might - want to know what your project looked like last Tuesday, or - you could be looking through changesets to see which one - introduced a bug. In cases like this, the natural thing to do - is update the working directory to the changeset you're - interested in, and then examine the files in the working - directory directly to see their contents as they were when you - committed that changeset. The effect of this is shown in - figure <xref endterm="fig.concepts.wdir-pre-branch.caption" - linkend="fig.concepts.wdir-pre-branch"/>.</para> - - <informalfigure id="fig.concepts.wdir-pre-branch"> - <mediaobject> - <imageobject><imagedata fileref="images/wdir-pre-branch.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.wdir-pre-branch.caption">The working - directory, updated to an older changeset</para></caption> - </mediaobject> - </informalfigure> - - <para>Having updated the working directory to an older - changeset, what happens if you make some changes, and then - commit? Mercurial behaves in the same way as I outlined - above. The parents of the working directory become the - parents of the new changeset. This new changeset has no - children, so it becomes the new tip. And the repository now - contains two changesets that have no children; we call these - <emphasis>heads</emphasis>. You can see the structure that - this creates in figure <xref - endterm="fig.concepts.wdir-branch.caption" - linkend="fig.concepts.wdir-branch"/>.</para> - - <informalfigure id="fig.concepts.wdir-branch"> - <mediaobject> - <imageobject><imagedata fileref="images/wdir-branch.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.wdir-branch.caption">After a - commit made while synced to an older changeset</para></caption> - </mediaobject> - </informalfigure> - - <note> - <para> If you're new to Mercurial, you should keep in mind a - common <quote>error</quote>, which is to use the <command - role="hg-cmd">hg pull</command> command without any - options. By default, the <command role="hg-cmd">hg - pull</command> command <emphasis>does not</emphasis> - update the working directory, so you'll bring new changesets - into your repository, but the working directory will stay - synced at the same changeset as before the pull. If you - make some changes and commit afterwards, you'll thus create - a new head, because your working directory isn't synced to - whatever the current tip is.</para> - - <para> I put the word <quote>error</quote> in quotes because - all that you need to do to rectify this situation is - <command role="hg-cmd">hg merge</command>, then <command - role="hg-cmd">hg commit</command>. In other words, this - almost never has negative consequences; it just surprises - people. I'll discuss other ways to avoid this behaviour, - and why Mercurial behaves in this initially surprising way, - later on.</para> - </note> - - </sect2> - <sect2> - <title>Merging heads</title> - - <para>When you run the <command role="hg-cmd">hg merge</command> - command, Mercurial leaves the first parent of the working - directory unchanged, and sets the second parent to the - changeset you're merging with, as shown in figure <xref - endterm="fig.concepts.wdir-merge.caption" - linkend="fig.concepts.wdir-merge"/>.</para> - - <informalfigure id="fig.concepts.wdir-merge"> - <mediaobject> - <imageobject><imagedata fileref="images/wdir-merge.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.concepts.wdir-merge.caption">Merging two - heads</para></caption> - </mediaobject> - </informalfigure> - - <para>Mercurial also has to modify the working directory, to - merge the files managed in the two changesets. Simplified a - little, the merging process goes like this, for every file in - the manifests of both changesets.</para> - <itemizedlist> - <listitem><para>If neither changeset has modified a file, do - nothing with that file.</para> - </listitem> - <listitem><para>If one changeset has modified a file, and the - other hasn't, create the modified copy of the file in the - working directory.</para> - </listitem> - <listitem><para>If one changeset has removed a file, and the - other hasn't (or has also deleted it), delete the file - from the working directory.</para> - </listitem> - <listitem><para>If one changeset has removed a file, but the - other has modified the file, ask the user what to do: keep - the modified file, or remove it?</para> - </listitem> - <listitem><para>If both changesets have modified a file, - invoke an external merge program to choose the new - contents for the merged file. This may require input from - the user.</para> - </listitem> - <listitem><para>If one changeset has modified a file, and the - other has renamed or copied the file, make sure that the - changes follow the new name of the file.</para> - </listitem></itemizedlist> - <para>There are more details&emdash;merging has plenty of corner - cases&emdash;but these are the most common choices that are - involved in a merge. As you can see, most cases are - completely automatic, and indeed most merges finish - automatically, without requiring your input to resolve any - conflicts.</para> - - <para>When you're thinking about what happens when you commit - after a merge, once again the working directory is <quote>the - changeset I'm about to commit</quote>. After the <command - role="hg-cmd">hg merge</command> command completes, the - working directory has two parents; these will become the - parents of the new changeset.</para> - - <para>Mercurial lets you perform multiple merges, but you must - commit the results of each individual merge as you go. This - is necessary because Mercurial only tracks two parents for - both revisions and the working directory. While it would be - technically possible to merge multiple changesets at once, the - prospect of user confusion and making a terrible mess of a - merge immediately becomes overwhelming.</para> - - </sect2> - </sect1> - <sect1> - <title>Other interesting design features</title> - - <para>In the sections above, I've tried to highlight some of the - most important aspects of Mercurial's design, to illustrate that - it pays careful attention to reliability and performance. - However, the attention to detail doesn't stop there. There are - a number of other aspects of Mercurial's construction that I - personally find interesting. I'll detail a few of them here, - separate from the <quote>big ticket</quote> items above, so that - if you're interested, you can gain a better idea of the amount - of thinking that goes into a well-designed system.</para> - - <sect2> - <title>Clever compression</title> - - <para>When appropriate, Mercurial will store both snapshots and - deltas in compressed form. It does this by always - <emphasis>trying to</emphasis> compress a snapshot or delta, - but only storing the compressed version if it's smaller than - the uncompressed version.</para> - - <para>This means that Mercurial does <quote>the right - thing</quote> when storing a file whose native form is - compressed, such as a <literal>zip</literal> archive or a JPEG - image. When these types of files are compressed a second - time, the resulting file is usually bigger than the - once-compressed form, and so Mercurial will store the plain - <literal>zip</literal> or JPEG.</para> - - <para>Deltas between revisions of a compressed file are usually - larger than snapshots of the file, and Mercurial again does - <quote>the right thing</quote> in these cases. It finds that - such a delta exceeds the threshold at which it should store a - complete snapshot of the file, so it stores the snapshot, - again saving space compared to a naive delta-only - approach.</para> - - <sect3> - <title>Network recompression</title> - - <para>When storing revisions on disk, Mercurial uses the - <quote>deflate</quote> compression algorithm (the same one - used by the popular <literal>zip</literal> archive format), - which balances good speed with a respectable compression - ratio. However, when transmitting revision data over a - network connection, Mercurial uncompresses the compressed - revision data.</para> - - <para>If the connection is over HTTP, Mercurial recompresses - the entire stream of data using a compression algorithm that - gives a better compression ratio (the Burrows-Wheeler - algorithm from the widely used <literal>bzip2</literal> - compression package). This combination of algorithm and - compression of the entire stream (instead of a revision at a - time) substantially reduces the number of bytes to be - transferred, yielding better network performance over almost - all kinds of network.</para> - - <para>(If the connection is over <command>ssh</command>, - Mercurial <emphasis>doesn't</emphasis> recompress the - stream, because <command>ssh</command> can already do this - itself.)</para> - - </sect3> - </sect2> - <sect2> - <title>Read/write ordering and atomicity</title> - - <para>Appending to files isn't the whole story when it comes to - guaranteeing that a reader won't see a partial write. If you - recall figure <xref endterm="fig.concepts.metadata.caption" - linkend="fig.concepts.metadata"/>, revisions in the - changelog point to revisions in the manifest, and revisions in - the manifest point to revisions in filelogs. This hierarchy - is deliberate.</para> - - <para>A writer starts a transaction by writing filelog and - manifest data, and doesn't write any changelog data until - those are finished. A reader starts by reading changelog - data, then manifest data, followed by filelog data.</para> - - <para>Since the writer has always finished writing filelog and - manifest data before it writes to the changelog, a reader will - never read a pointer to a partially written manifest revision - from the changelog, and it will never read a pointer to a - partially written filelog revision from the manifest.</para> - - </sect2> - <sect2> - <title>Concurrent access</title> - - <para>The read/write ordering and atomicity guarantees mean that - Mercurial never needs to <emphasis>lock</emphasis> a - repository when it's reading data, even if the repository is - being written to while the read is occurring. This has a big - effect on scalability; you can have an arbitrary number of - Mercurial processes safely reading data from a repository - safely all at once, no matter whether it's being written to or - not.</para> - - <para>The lockless nature of reading means that if you're - sharing a repository on a multi-user system, you don't need to - grant other local users permission to - <emphasis>write</emphasis> to your repository in order for - them to be able to clone it or pull changes from it; they only - need <emphasis>read</emphasis> permission. (This is - <emphasis>not</emphasis> a common feature among revision - control systems, so don't take it for granted! Most require - readers to be able to lock a repository to access it safely, - and this requires write permission on at least one directory, - which of course makes for all kinds of nasty and annoying - security and administrative problems.)</para> - - <para>Mercurial uses locks to ensure that only one process can - write to a repository at a time (the locking mechanism is safe - even over filesystems that are notoriously hostile to locking, - such as NFS). If a repository is locked, a writer will wait - for a while to retry if the repository becomes unlocked, but - if the repository remains locked for too long, the process - attempting to write will time out after a while. This means - that your daily automated scripts won't get stuck forever and - pile up if a system crashes unnoticed, for example. (Yes, the - timeout is configurable, from zero to infinity.)</para> - - <sect3> - <title>Safe dirstate access</title> - - <para>As with revision data, Mercurial doesn't take a lock to - read the dirstate file; it does acquire a lock to write it. - To avoid the possibility of reading a partially written copy - of the dirstate file, Mercurial writes to a file with a - unique name in the same directory as the dirstate file, then - renames the temporary file atomically to - <filename>dirstate</filename>. The file named - <filename>dirstate</filename> is thus guaranteed to be - complete, not partially written.</para> - - </sect3> - </sect2> - <sect2> - <title>Avoiding seeks</title> - - <para>Critical to Mercurial's performance is the avoidance of - seeks of the disk head, since any seek is far more expensive - than even a comparatively large read operation.</para> - - <para>This is why, for example, the dirstate is stored in a - single file. If there were a dirstate file per directory that - Mercurial tracked, the disk would seek once per directory. - Instead, Mercurial reads the entire single dirstate file in - one step.</para> - - <para>Mercurial also uses a <quote>copy on write</quote> scheme - when cloning a repository on local storage. Instead of - copying every revlog file from the old repository into the new - repository, it makes a <quote>hard link</quote>, which is a - shorthand way to say <quote>these two names point to the same - file</quote>. When Mercurial is about to write to one of a - revlog's files, it checks to see if the number of names - pointing at the file is greater than one. If it is, more than - one repository is using the file, so Mercurial makes a new - copy of the file that is private to this repository.</para> - - <para>A few revision control developers have pointed out that - this idea of making a complete private copy of a file is not - very efficient in its use of storage. While this is true, - storage is cheap, and this method gives the highest - performance while deferring most book-keeping to the operating - system. An alternative scheme would most likely reduce - performance and increase the complexity of the software, each - of which is much more important to the <quote>feel</quote> of - day-to-day use.</para> - - </sect2> - <sect2> - <title>Other contents of the dirstate</title> - - <para>Because Mercurial doesn't force you to tell it when you're - modifying a file, it uses the dirstate to store some extra - information so it can determine efficiently whether you have - modified a file. For each file in the working directory, it - stores the time that it last modified the file itself, and the - size of the file at that time.</para> - - <para>When you explicitly <command role="hg-cmd">hg - add</command>, <command role="hg-cmd">hg remove</command>, - <command role="hg-cmd">hg rename</command> or <command - role="hg-cmd">hg copy</command> files, Mercurial updates the - dirstate so that it knows what to do with those files when you - commit.</para> - - <para>When Mercurial is checking the states of files in the - working directory, it first checks a file's modification time. - If that has not changed, the file must not have been modified. - If the file's size has changed, the file must have been - modified. If the modification time has changed, but the size - has not, only then does Mercurial need to read the actual - contents of the file to see if they've changed. Storing these - few extra pieces of information dramatically reduces the - amount of data that Mercurial needs to read, which yields - large performance improvements compared to other revision - control systems.</para> - - </sect2> - </sect1> -</chapter> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch04-daily.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,544 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<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 <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 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 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>.</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>a</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, 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 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 behaviour is common to most Mercurial commands.</para> + + </sect2> + <sect2> + <title>Aside: 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. 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 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 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> + <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 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 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 repository that you're + pulling changes from <emphasis>doesn't know</emphasis> about + 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 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 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 + until this point, and no further.</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 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 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 + behaviour 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). 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.</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 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>.</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 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 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>.</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 behaviour 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 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 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'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 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 chapter <xref + linkend="chap.undo"/>.</para> + + </sect1> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch05-collab.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,1437 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="cha.collab"> + <?dbhtml filename="collaborating-with-other-people.html"?> + <title>Collaborating with other people</title> + + <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 require 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.</para> + + &interaction.branching.init; + + <para>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.</para> + + &interaction.branching.tag; + + <para>Let's say some ongoing + development occurs on the main branch.</para> + + &interaction.branching.main; + + <para>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>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.</para> + + &interaction.branching.clone; + + <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.</para> + + &interaction.branching.stable; + + <para>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.</para> + + &interaction.branching.merge; + + <para>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 id="fig.collab.feature-branches"> + <mediaobject> + <imageobject><imagedata fileref="images/feature-branches.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.collab.feature-branches.caption">Feature + branches</para></caption> + </mediaobject> + </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 id="sec.collab.serve"> + <title>Informal sharing with <command role="hg-cmd">hg + serve</command></title> + + <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 id="sec.collab.ssh"> + <title>Using the Secure Shell (ssh) protocol</title> + + <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>ssh://bos@hg.serpentine.com:22/hg/hgbook</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 a colon and port number 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>ssh://server/~otheruser/hg/repo</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>ssh://server//absolute/path</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 <filename + role="special">hg.ini</filename> 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>[ui] +ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key"</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>ssh myserver date</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>ssh myserver hg version</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>[ui] +ssh = ssh -C</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>Host hg + Compression yes + HostName hg.example.com</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 id="sec.collab.cgi"> + <title>Serving over HTTP using CGI</title> + + <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/username/foo</literal>.</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>cp .../hgweb.cgi ~/public_html +chmod 755 ~/public_html/hgweb.cgi</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>chmod 755 ~/public_html</programlisting> + + <sect3 id="sec.collab.wtf"> + <title>What could <emphasis>possibly</emphasis> go + wrong?</title> + + <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>chmod 755 ~ +find ~/public_html -type d -print0 | xargs -0r chmod 755 +find ~/public_html -type f -print0 | xargs -0r chmod 644</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 <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>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><![CDATA[&ch06-apache-config.lst;]]></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>AddHandler cgi-script .cgi</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>userdir.path = "public_html" +cgi.assign = (".cgi" => "" )</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>cp .../hgwebdir.cgi ~/public_html +chmod 755 ~/public_html ~/public_html/hgwebdir.cgi</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>[collections] +/my/root = /my/root</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>[paths] +repo1 = /my/path/to/some/repo +repo2 = /some/path/to/another</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> + <itemizedlist> + <listitem><para><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><literal>gz</literal>: A + <command>tar</command> archive, compressed using + <literal>gzip</literal> compression.</para> + </listitem> + <listitem><para><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>[web] +allow_archive = bz2 gz zip</programlisting> + </listitem> + <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> + <programlisting>[web] +style = gitweb</programlisting> + </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> + + </sect3> + <sect3> + <title>Choosing the right <filename + role="special">~/.hgrc</filename> file to add <literal + role="rc-web">web</literal> items to</title> + + <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> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- a/en/ch05-daily.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,544 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<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>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>.</para> - - &interaction.daily.files.add; - - <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>.</para> - - &interaction.daily.files.add-dir; - - <para>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 - below.</para> - -&interaction.daily.files.hidden; - - <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>.</para> - - &interaction.daily.files.remove; - - <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.</para> - - &interaction.daily.files.missing; - - <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.</para> - - &interaction.daily.files.remove-after; - - <para>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>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>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>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> - <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.</para> - - &interaction.daily.copy.init; - - <para>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>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>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>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>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>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>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.</para> - - &interaction.daily.copy.simple; - - <para>If the destination is a directory, Mercurial copies its - sources into that directory.</para> - - &interaction.daily.copy.dir-dest; - - <para>Copying a directory is - recursive, and preserves the directory structure of the - source.</para> - - &interaction.daily.copy.dir-src; - - <para>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>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>.</para> - - &interaction.daily.copy.after; - - </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.</para> - - &interaction.daily.rename.rename; - - <para>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>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>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> - - &interaction.rename.divergent.clone; - - <para>Anne renames the file to <filename>bar</filename>.</para> - - &interaction.rename.divergent.rename.anne; - - <para>Meanwhile, Bob renames it to - <filename>quux</filename>.</para> - - &interaction.rename.divergent.rename.bob; - - <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.</para> - - &interaction.rename.divergent.merge; - - <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>.</para> - - &interaction.issue29.go; - - </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> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- a/en/ch06-collab.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1437 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="cha.collab"> - <?dbhtml filename="collaborating-with-other-people.html"?> - <title>Collaborating with other people</title> - - <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 require 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.</para> - - &interaction.branching.init; - - <para>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.</para> - - &interaction.branching.tag; - - <para>Let's say some ongoing - development occurs on the main branch.</para> - - &interaction.branching.main; - - <para>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>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.</para> - - &interaction.branching.clone; - - <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.</para> - - &interaction.branching.stable; - - <para>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.</para> - - &interaction.branching.merge; - - <para>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 id="fig.collab.feature-branches"> - <mediaobject> - <imageobject><imagedata fileref="images/feature-branches.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.collab.feature-branches.caption">Feature - branches</para></caption> - </mediaobject> - </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 id="sec.collab.serve"> - <title>Informal sharing with <command role="hg-cmd">hg - serve</command></title> - - <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 id="sec.collab.ssh"> - <title>Using the Secure Shell (ssh) protocol</title> - - <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>ssh://bos@hg.serpentine.com:22/hg/hgbook</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 a colon and port number 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>ssh://server/~otheruser/hg/repo</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>ssh://server//absolute/path</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 <filename - role="special">hg.ini</filename> 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>[ui] -ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key"</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>ssh myserver date</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>ssh myserver hg version</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>[ui] -ssh = ssh -C</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>Host hg - Compression yes - HostName hg.example.com</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 id="sec.collab.cgi"> - <title>Serving over HTTP using CGI</title> - - <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/username/foo</literal>.</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>cp .../hgweb.cgi ~/public_html -chmod 755 ~/public_html/hgweb.cgi</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>chmod 755 ~/public_html</programlisting> - - <sect3 id="sec.collab.wtf"> - <title>What could <emphasis>possibly</emphasis> go - wrong?</title> - - <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>chmod 755 ~ -find ~/public_html -type d -print0 | xargs -0r chmod 755 -find ~/public_html -type f -print0 | xargs -0r chmod 644</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 <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>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><![CDATA[&ch06-apache-config.lst;]]></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>AddHandler cgi-script .cgi</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>userdir.path = "public_html" -cgi.assign = (".cgi" => "" )</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>cp .../hgwebdir.cgi ~/public_html -chmod 755 ~/public_html ~/public_html/hgwebdir.cgi</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>[collections] -/my/root = /my/root</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>[paths] -repo1 = /my/path/to/some/repo -repo2 = /some/path/to/another</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> - <itemizedlist> - <listitem><para><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><literal>gz</literal>: A - <command>tar</command> archive, compressed using - <literal>gzip</literal> compression.</para> - </listitem> - <listitem><para><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>[web] -allow_archive = bz2 gz zip</programlisting> - </listitem> - <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> - <programlisting>[web] -style = gitweb</programlisting> - </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> - - </sect3> - <sect3> - <title>Choosing the right <filename - role="special">~/.hgrc</filename> file to add <literal - role="rc-web">web</literal> items to</title> - - <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> -</chapter> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch06-filenames.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,408 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<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 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 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 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>.</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>, + 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 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>Ignoring unwanted files and directories</title> + + <para id="x_569">XXX.</para> + + </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 {FoO} 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> + + <note> + <para id="x_577"> 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> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch07-branch.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,533 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<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 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, 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 + <emphasis>will not contain the history that created the + tag</emphasis> 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 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 a + 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 + 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 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.</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: +-->
--- a/en/ch07-filenames.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,408 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.names"> - <?dbhtml filename="file-names-and-pattern-matching.html"?> - <title>File names and pattern matching</title> - - <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.</para> - - &interaction.filenames.dirs; - - </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>.</para> - - &interaction.filenames.wdir-subdir; - - <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.</para> - - &interaction.filenames.wdir-relname; - - </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>syntax:patternbody</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.</para> - - &interaction.filenames.glob.star; - - <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.</para> - - &interaction.filenames.glob.starstar; - - <para>The <quote><literal>?</literal></quote> pattern matches - any single character.</para> - - &interaction.filenames.glob.question; - - <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>.</para> - - &interaction.filenames.glob.range; - - <para>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><literal>}</literal></quote> ends the group.</para> - - &interaction.filenames.glob.group; - - <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.</para> - - &interaction.filenames.glob.star-starstar; - - </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>.</para> - - &interaction.filenames.filter.include; - - <para>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>Ignoring unwanted files and directories</title> - - <para>XXX.</para> - - </sect1> - <sect1 id="sec.names.case"> - <title>Case sensitivity</title> - - <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 <emphasis>case - folding</emphasis>.</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> -</chapter> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- a/en/ch08-branch.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,533 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.branch"> - <?dbhtml filename="managing-releases-and-branchy-development.html"?> - <title>Managing releases and branchy development</title> - - <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>.</para> - - &interaction.tag.tag; - - <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.</para> - - &interaction.tag.tags; - - <para>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.</para> - - &interaction.tag.log; - - <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.</para> - - &interaction.tag.log.v1.0; - - <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>.</para> - - &interaction.tag.remove; - - <para>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>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>.</para> - - &interaction.tag.tip; - - <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 - <emphasis>will not contain the history that created the - tag</emphasis> 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.</para> - - &interaction.branch-repo.tag; - - <para>You can then clone a new shared - <literal>myproject-1.0.1</literal> repository as of that - tag.</para> - - &interaction.branch-repo.clone; - - <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.</para> - - &interaction.branch-repo.bugfix; - - <para>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>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.</para> - - &interaction.branch-repo.pull; - - <para>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>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.</para> - - &interaction.branch-named.branches; - - <para>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.</para> - - &interaction.branch-named.branch; - - <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.</para> - - &interaction.branch-named.create; - - <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?</para> - - &interaction.branch-named.status; - - <para>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.</para> - - &interaction.branch-named.commit; - - <para>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.</para> - - &interaction.branch-named.rebranch; - - <para>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.</para> - - &interaction.branch-named.parents; - - <para>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.</para> - - &interaction.branch-named.update-switchy; - - <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>.</para> - - &interaction.branch-named.update-nothing; - - <para>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>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.</para> - - &interaction.branch-named.merge; - - <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>[hooks] -pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch</programlisting> - - </sect1> -</chapter> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch08-undo.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,1083 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.undo"> + <?dbhtml filename="finding-and-fixing-mistakes.html"?> + <title>Finding and fixing mistakes</title> + + <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 id="sec.undo.rollback"> + <title>Rolling back a transaction</title> + + <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.</para> + + &interaction.rollback.commit; + + <para>Looking at the output of <command role="hg-cmd">hg + status</command> after the commit immediately confirms the + error.</para> + + &interaction.rollback.status; + + <para>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.</para> + + &interaction.rollback.rollback; + + <para>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>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 id="sec.undo.rollback-after-push"> + <title>Rolling back is useless once you've pushed</title> + + <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.</para> + + &interaction.rollback.twice; + + <para>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.</para> + + &interaction.daily.revert.modify; + + <para>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>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; + + <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 id="sec.undo.mgmt"> + <title>File management errors</title> + + <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.</para> + + &interaction.daily.revert.add; + + <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>).</para> + + &interaction.daily.revert.missing; + + <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.</para> + + &interaction.daily.revert.copy; + + <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.</para> + + &interaction.daily.revert.rename; + + <para>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.</para> + + &interaction.daily.revert.rename-orig; + + <para>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.</para> + + &interaction.backout.init; + + <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.</para> + + &interaction.backout.simple; + + <para>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 + endterm="fig.undo.backout.caption" 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 id="fig.undo.backout"> + <mediaobject> + <imageobject><imagedata fileref="images/undo-simple.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.undo.backout.caption">Backing out + a change using the + <command role="hg-cmd">hg backout</command> + command</para></caption> + </mediaobject> + </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.</para> + + &interaction.backout.non-tip.clone; + + <para>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>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>As the graphical history in figure <xref + endterm="fig.undo.backout-non-tip.caption" + 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 id="fig.undo.backout-non-tip"> + <mediaobject> + <imageobject><imagedata fileref="images/undo-non-tip.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.undo.backout-non-tip.caption">Automated + backout of a non-tip change using the + <command role="hg-cmd">hg backout</command> command</para></caption> + </mediaobject> + </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> + + &interaction.backout.manual.clone; + + <para>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>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>Again, it's easier to see what has happened by looking at + a graph of the revision history, in figure <xref + endterm="fig.undo.backout-manual.caption" + 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 id="fig.undo.backout-manual"> + <mediaobject> + <imageobject><imagedata fileref="images/undo-manual.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.undo.backout-manual.caption">Backing out a + change using the <command role="hg-cmd">hg backout</command> + command</para></caption> + </mediaobject> + </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.</para> + + &interaction.backout.manual.parents; + + <para>Now we have two isolated sets of changes.</para> + + &interaction.backout.manual.heads; + + <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>.</para> + + &interaction.backout.manual.cat; + + <para>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>Afterwards, the graphical history of our repository looks + like figure + <xref endterm="fig.undo.backout-manual-merge.caption" + linkend="fig.undo.backout-manual-merge"/>.</para> + + <informalfigure id="fig.undo.backout-manual-merge"> + <mediaobject> + <imageobject><imagedata fileref="images/undo-manual-merge.png"/> + </imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.undo.backout-manual-merge.caption">Manually + merging a backout change</para></caption> + </mediaobject> + </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 id="sec.undo.aaaiiieee"> + <title>Changes that should never have been</title> + + <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 id="sec.undo.bisect"> + <title>Finding the source of a bug</title> + + <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</quote>.</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.</para> + + &interaction.bisect.init; + + <para>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>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>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> + <itemizedlist> + <listitem><para>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>If it failed, run the <command + role="hg-cmd">hg bisect bad</command> + command.</para></listitem></itemizedlist> + </listitem> + <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.</para> + + &interaction.bisect.search.init; + + <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>.</para> + + &interaction.bisect.search.bad-init; + + <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.)</para> + + &interaction.bisect.search.good-init; + + <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.</para> + &interaction.bisect.search.mytest; + + <para>We can now run an entire test step with a single command, + <literal>mytest</literal>.</para> + + &interaction.bisect.search.step2; + + <para>A few more invocations of our canned test step command, + and we're done.</para> + + &interaction.bisect.search.rest; + + <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>.</para> + + &interaction.bisect.search.reset; + + </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> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch09-hook.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,2037 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.hook"> + <?dbhtml filename="handling-repository-events-with-hooks.html"?> + <title>Handling repository events with hooks</title> + + <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 id="sec.hook.simple"> + <title>A short tutorial on using hooks</title> + + <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> + + <para>All hooks follow the pattern in this example.</para> + +&interaction.hook.simple.init; + + <para>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 below.</para> + +&interaction.hook.simple.ext; + + <para>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>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 id="sec.hook.perm"> + <title>Controlling whether an activity can proceed</title> + + <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> + +&interaction.hook.simple.pretxncommit; + + <para>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>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>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 id="sec.hook.param"> + <title>Hook parameters</title> + + <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>[hooks] +commit.example = python:mymodule.submodule.myhook</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>def myhook(ui, repo, **kwargs): + pass</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 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>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> + +&interaction.hook.ws.simple; + + <para>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> + + <para>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.hook.ws.better; + + <para>As a final aside, note in the example above 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>perl -pi -e 's,\s+$,,' filename</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>[hooks] +pretxnchangegroup.acl = python:hgext.acl.hook</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>[acl.allow] +docs/** = docwriter +[acl.deny] +source/sensitive/** = intern</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>[ui] +debug = true</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>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>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 id="sec.hook.bugzilla.config"> + <title>Configuring the <literal role="hook">bugzilla</literal> + hook</title> + + <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>[hooks] +incoming.bugzilla = python:hgext.bugzilla.hook</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> + <programlisting>cd /var/www/html/bugzilla && + ./processmail %s nobody@nowhere.com</programlisting> + </listitem> + <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>[usermap] +jane.user@example.com = jane</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># regular hgrc file refers to external usermap file +[bugzilla] +usermap = /home/hg/repos/userdata/bugzilla-usermap.conf</programlisting> + <para>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>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>[web] +baseurl = http://hg.domain.com/</programlisting> + + <para>Here is an example set of <literal + role="hg-ext">bugzilla</literal> hook config information. + </para> + + <programlisting>&ch10-bugzilla-config.lst;</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>hg_user = (httpd_user) +NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s</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>#!/bin/sh +cd `dirname $0` && ./processmail "$1" nobody@example.com</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>cannot find bugzilla user id for john.q.public@example.com</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>[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>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>&ch10-notify-config.lst;</programlisting> + + <para>This will produce a message that looks like the + following: + </para> + + <programlisting>&ch10-notify-config-mail.lst;</programlisting> + + </sect3> + <sect3> + <title>Testing and troubleshooting</title> + + <para>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>An in-process hook is called with arguments of the + following form: + </para> + <programlisting>def myhook(ui, repo, **kwargs): pass</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>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>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 <literal>bin</literal> 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 id="sec.hook.sources"> + <title>Sources of changesets</title> + + <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 id="sec.hook.url"> + <title>Where changes are going&emdash;remote repository + URLs</title> + + <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:1.2.3.4</literal>&emdash;remote + ssh client, at the IP address + <literal>1.2.3.4</literal>. + </para> + </listitem> + <listitem><para><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>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>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 + <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><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 id="sec.hook.commit"> + <title><literal role="hook">commit</literal>&emdash;after a new + changeset is created</title> + + <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 id="sec.hook.incoming"> + <title><literal role="hook">incoming</literal>&emdash;after one + remote changeset is added</title> + + <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 id="sec.hook.outgoing"> + <title><literal role="hook">outgoing</literal>&emdash;after + changesets are propagated</title> + + <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 id="sec.hook.prechangegroup"> + <title><literal + role="hook">prechangegroup</literal>&emdash;before starting + to add remote changesets</title> + + <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 id="sec.hook.precommit"> + <title><literal role="hook">precommit</literal>&emdash;before + starting to commit a changeset</title> + + <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 id="sec.hook.preoutgoing"> + <title><literal role="hook">preoutgoing</literal>&emdash;before + starting to propagate changesets</title> + + <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 id="sec.hook.pretag"> + <title><literal role="hook">pretag</literal>&emdash;before + tagging a changeset</title> + + <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> + </sect2> + <sect2 id="sec.hook.pretxnchangegroup"> + <title><literal + role="hook">pretxnchangegroup</literal>&emdash;before + completing addition of remote changesets</title> + + <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 + <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><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 id="sec.hook.pretxncommit"> + <title><literal role="hook">pretxncommit</literal>&emdash;before + completing commit of new changeset</title> + + <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 id="sec.hook.preupdate"> + <title><literal role="hook">preupdate</literal>&emdash;before + updating or merging working directory</title> + + <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 id="sec.hook.tag"> + <title><literal role="hook">tag</literal>&emdash;after tagging a + changeset</title> + + <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 id="sec.hook.update"> + <title><literal role="hook">update</literal>&emdash;after + updating or merging working directory</title> + + <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> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- a/en/ch09-undo.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1083 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.undo"> - <?dbhtml filename="finding-and-fixing-mistakes.html"?> - <title>Finding and fixing mistakes</title> - - <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 id="sec.undo.rollback"> - <title>Rolling back a transaction</title> - - <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.</para> - - &interaction.rollback.commit; - - <para>Looking at the output of <command role="hg-cmd">hg - status</command> after the commit immediately confirms the - error.</para> - - &interaction.rollback.status; - - <para>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.</para> - - &interaction.rollback.rollback; - - <para>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>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 id="sec.undo.rollback-after-push"> - <title>Rolling back is useless once you've pushed</title> - - <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.</para> - - &interaction.rollback.twice; - - <para>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.</para> - - &interaction.daily.revert.modify; - - <para>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>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; - - <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 id="sec.undo.mgmt"> - <title>File management errors</title> - - <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.</para> - - &interaction.daily.revert.add; - - <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>).</para> - - &interaction.daily.revert.missing; - - <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.</para> - - &interaction.daily.revert.copy; - - <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.</para> - - &interaction.daily.revert.rename; - - <para>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.</para> - - &interaction.daily.revert.rename-orig; - - <para>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.</para> - - &interaction.backout.init; - - <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.</para> - - &interaction.backout.simple; - - <para>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 - endterm="fig.undo.backout.caption" 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 id="fig.undo.backout"> - <mediaobject> - <imageobject><imagedata fileref="images/undo-simple.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.undo.backout.caption">Backing out - a change using the - <command role="hg-cmd">hg backout</command> - command</para></caption> - </mediaobject> - </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.</para> - - &interaction.backout.non-tip.clone; - - <para>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>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>As the graphical history in figure <xref - endterm="fig.undo.backout-non-tip.caption" - 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 id="fig.undo.backout-non-tip"> - <mediaobject> - <imageobject><imagedata fileref="images/undo-non-tip.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.undo.backout-non-tip.caption">Automated - backout of a non-tip change using the - <command role="hg-cmd">hg backout</command> command</para></caption> - </mediaobject> - </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> - - &interaction.backout.manual.clone; - - <para>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>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>Again, it's easier to see what has happened by looking at - a graph of the revision history, in figure <xref - endterm="fig.undo.backout-manual.caption" - 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 id="fig.undo.backout-manual"> - <mediaobject> - <imageobject><imagedata fileref="images/undo-manual.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.undo.backout-manual.caption">Backing out a - change using the <command role="hg-cmd">hg backout</command> - command</para></caption> - </mediaobject> - </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.</para> - - &interaction.backout.manual.parents; - - <para>Now we have two isolated sets of changes.</para> - - &interaction.backout.manual.heads; - - <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>.</para> - - &interaction.backout.manual.cat; - - <para>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>Afterwards, the graphical history of our repository looks - like figure - <xref endterm="fig.undo.backout-manual-merge.caption" - linkend="fig.undo.backout-manual-merge"/>.</para> - - <informalfigure id="fig.undo.backout-manual-merge"> - <mediaobject> - <imageobject><imagedata fileref="images/undo-manual-merge.png"/> - </imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.undo.backout-manual-merge.caption">Manually - merging a backout change</para></caption> - </mediaobject> - </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 id="sec.undo.aaaiiieee"> - <title>Changes that should never have been</title> - - <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 id="sec.undo.bisect"> - <title>Finding the source of a bug</title> - - <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</quote>.</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.</para> - - &interaction.bisect.init; - - <para>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>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>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> - <itemizedlist> - <listitem><para>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>If it failed, run the <command - role="hg-cmd">hg bisect bad</command> - command.</para></listitem></itemizedlist> - </listitem> - <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.</para> - - &interaction.bisect.search.init; - - <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>.</para> - - &interaction.bisect.search.bad-init; - - <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.)</para> - - &interaction.bisect.search.good-init; - - <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.</para> - &interaction.bisect.search.mytest; - - <para>We can now run an entire test step with a single command, - <literal>mytest</literal>.</para> - - &interaction.bisect.search.step2; - - <para>A few more invocations of our canned test step command, - and we're done.</para> - - &interaction.bisect.search.rest; - - <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>.</para> - - &interaction.bisect.search.reset; - - </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> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- a/en/ch10-hook.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2037 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.hook"> - <?dbhtml filename="handling-repository-events-with-hooks.html"?> - <title>Handling repository events with hooks</title> - - <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 id="sec.hook.simple"> - <title>A short tutorial on using hooks</title> - - <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> - - <para>All hooks follow the pattern in this example.</para> - -&interaction.hook.simple.init; - - <para>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 below.</para> - -&interaction.hook.simple.ext; - - <para>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>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 id="sec.hook.perm"> - <title>Controlling whether an activity can proceed</title> - - <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> - -&interaction.hook.simple.pretxncommit; - - <para>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>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>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 id="sec.hook.param"> - <title>Hook parameters</title> - - <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>[hooks] -commit.example = python:mymodule.submodule.myhook</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>def myhook(ui, repo, **kwargs): - pass</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 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>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> - -&interaction.hook.ws.simple; - - <para>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> - - <para>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.hook.ws.better; - - <para>As a final aside, note in the example above 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>perl -pi -e 's,\s+$,,' filename</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>[hooks] -pretxnchangegroup.acl = python:hgext.acl.hook</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>[acl.allow] -docs/** = docwriter -[acl.deny] -source/sensitive/** = intern</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>[ui] -debug = true</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>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>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 id="sec.hook.bugzilla.config"> - <title>Configuring the <literal role="hook">bugzilla</literal> - hook</title> - - <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>[hooks] -incoming.bugzilla = python:hgext.bugzilla.hook</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> - <programlisting>cd /var/www/html/bugzilla && - ./processmail %s nobody@nowhere.com</programlisting> - </listitem> - <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>[usermap] -jane.user@example.com = jane</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># regular hgrc file refers to external usermap file -[bugzilla] -usermap = /home/hg/repos/userdata/bugzilla-usermap.conf</programlisting> - <para>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>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>[web] -baseurl = http://hg.domain.com/</programlisting> - - <para>Here is an example set of <literal - role="hg-ext">bugzilla</literal> hook config information. - </para> - - <programlisting>&ch10-bugzilla-config.lst;</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>hg_user = (httpd_user) -NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s</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>#!/bin/sh -cd `dirname $0` && ./processmail "$1" nobody@example.com</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>cannot find bugzilla user id for john.q.public@example.com</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>[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>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>&ch10-notify-config.lst;</programlisting> - - <para>This will produce a message that looks like the - following: - </para> - - <programlisting>&ch10-notify-config-mail.lst;</programlisting> - - </sect3> - <sect3> - <title>Testing and troubleshooting</title> - - <para>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>An in-process hook is called with arguments of the - following form: - </para> - <programlisting>def myhook(ui, repo, **kwargs): pass</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>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>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 <literal>bin</literal> 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 id="sec.hook.sources"> - <title>Sources of changesets</title> - - <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 id="sec.hook.url"> - <title>Where changes are going&emdash;remote repository - URLs</title> - - <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:1.2.3.4</literal>&emdash;remote - ssh client, at the IP address - <literal>1.2.3.4</literal>. - </para> - </listitem> - <listitem><para><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>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>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 - <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><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 id="sec.hook.commit"> - <title><literal role="hook">commit</literal>&emdash;after a new - changeset is created</title> - - <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 id="sec.hook.incoming"> - <title><literal role="hook">incoming</literal>&emdash;after one - remote changeset is added</title> - - <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 id="sec.hook.outgoing"> - <title><literal role="hook">outgoing</literal>&emdash;after - changesets are propagated</title> - - <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 id="sec.hook.prechangegroup"> - <title><literal - role="hook">prechangegroup</literal>&emdash;before starting - to add remote changesets</title> - - <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 id="sec.hook.precommit"> - <title><literal role="hook">precommit</literal>&emdash;before - starting to commit a changeset</title> - - <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 id="sec.hook.preoutgoing"> - <title><literal role="hook">preoutgoing</literal>&emdash;before - starting to propagate changesets</title> - - <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 id="sec.hook.pretag"> - <title><literal role="hook">pretag</literal>&emdash;before - tagging a changeset</title> - - <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> - </sect2> - <sect2 id="sec.hook.pretxnchangegroup"> - <title><literal - role="hook">pretxnchangegroup</literal>&emdash;before - completing addition of remote changesets</title> - - <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 - <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><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 id="sec.hook.pretxncommit"> - <title><literal role="hook">pretxncommit</literal>&emdash;before - completing commit of new changeset</title> - - <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 id="sec.hook.preupdate"> - <title><literal role="hook">preupdate</literal>&emdash;before - updating or merging working directory</title> - - <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 id="sec.hook.tag"> - <title><literal role="hook">tag</literal>&emdash;after tagging a - changeset</title> - - <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 id="sec.hook.update"> - <title><literal role="hook">update</literal>&emdash;after - updating or merging working directory</title> - - <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> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch10-template.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,675 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.template"> + <?dbhtml filename="customizing-the-output-of-mercurial.html"?> + <title>Customising 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 customise 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 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 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 + section <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 section + <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 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 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 section <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 not guaranteed to be unique, but it is + nevertheless useful in many cases.</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>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: +-->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch11-mq.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,1323 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<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>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 id="sec.mq.history"> + <title>The prehistory of Mercurial Queues</title> + + <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 id="sec.mq.quilt"> + <title>A patchwork quilt</title> + + <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 id="sec.mq.quilt-mq"> + <title>From patchwork quilt to Mercurial Queues</title> + + <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 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>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 below for a simple example of these commands + in action.</para> + +&interaction.mq.dodiff.diff; + + <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 id="sec.mq.start"> + <title>Getting started with Mercurial Queues</title> + + <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 + below.</para> + + <programlisting>[extensions] +hgext.mq =</programlisting> + + <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.</para> + +&interaction.mq.qinit-help.help; + + <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.</para> + +&interaction.mq.tutorial.qinit; + + <para>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>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>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>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 + to update the patch you are working on.</para> + +&interaction.mq.tutorial.qrefresh; + + <para>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>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>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>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>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>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> + + </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 + endterm="fig.mq.stack.caption" linkend="fig.mq.stack"/> illustrates + the difference between applied and tracked patches.</para> + + <informalfigure id="fig.mq.stack"> + <mediaobject> + <imageobject><imagedata fileref="images/mq-stack.png"/></imageobject> + <textobject><phrase>XXX add text</phrase></textobject> + <caption><para id="fig.mq.stack.caption">Applied and unapplied patches + in the MQ patch stack</para></caption> + </mediaobject> + </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 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>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>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 section <xref linkend="sec.mq.perf"/> + below.)</para> + +&interaction.mq.tutorial.qpush-a; + + </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. 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>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>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 id="sec.mq.adv-patch"> + <title>More about patches</title> + + <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 -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>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 id="sec.mq.perf"> + <title>Getting the best performance out of MQ</title> + + <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">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>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 id="sec.mq.merge"> + <title>Updating your patches when the underlying code + changes</title> + + <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">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>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">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>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 <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>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>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.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>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 + <literal role="tag">qbase</literal> and + <literal role="tag">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> + <programlisting>hg email qbase:qtip</programlisting> + <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> + <programlisting>hg log -r foo.patch:qtip subdir</programlisting> + </listitem> + </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> + + <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. 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>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 id="sec.mq.repo"> + <title>Managing patches in a repository</title> + + <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">hg + -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">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> (<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>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>alias mq=`hg -R $(hg root)/.hg/patches'</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">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>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> + +&interaction.mq.tools.tools; + + <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.</para> + + &interaction.mq.tarball.download; + + <para>Continue by creating a patch stack and making your + changes.</para> + + &interaction.mq.tarball.qinit; + + <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.</para> + + &interaction.mq.tarball.newsource; + + <para>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.</para> + + &interaction.mq.tarball.repush; + + </sect2> + <sect2 id="sec.mq.combine"> + <title>Combining entire patches</title> + + <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.</para> + + &interaction.mq.tools.lsdiff; + + <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> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- a/en/ch11-template.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,675 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.template"> - <?dbhtml filename="customizing-the-output-of-mercurial.html"?> - <title>Customising the output of Mercurial</title> - - <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 id="sec.style"> - <title>Using precanned output styles</title> - - <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> - - &interaction.template.simple.normal; - - <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> - - &interaction.template.simple.compact; - - <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> - - &interaction.template.simple.changelog; - - <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>[ui] -style = compact</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> - - &interaction.template.simple.normal; - - <para>Now, let's run the same command, but using a template to - change its output.</para> - - &interaction.template.simple.simplest; - - <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> - - &interaction.template.simple.simplesub; - - <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><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 section - <xref - linkend="sec.template.escape"/>.</para> - - </sect1> - <sect1 id="sec.template.keyword"> - <title>Common template keywords</title> - - <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 below.</para> - -&interaction.template.simple.keywords; - - <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> - - &interaction.template.simple.datekeyword; - - </sect1> - <sect1 id="sec.template.escape"> - <title>Escape sequences</title> - - <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>\</literal>: - Backslash, <quote><literal>\</literal></quote>, ASCII - 134.</para> - </listitem> - <listitem><para><literal>\n</literal>: Newline, - ASCII 12.</para> - </listitem> - <listitem><para><literal>\r</literal>: Carriage - return, ASCII 15.</para> - </listitem> - <listitem><para><literal>\t</literal>: Tab, ASCII - 11.</para> - </listitem> - <listitem><para><literal>\v</literal>: Vertical - tab, ASCII 13.</para> - </listitem> - <listitem><para><literal>{</literal>: Open curly - brace, <quote><literal>{</literal></quote>, ASCII - 173.</para> - </listitem> - <listitem><para><literal>}</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 id="sec.template.filter"> - <title>Filtering keywords to change their results</title> - - <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> - -&interaction.template.simple.manyfilters; - - <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> - - &interaction.template.simple.combine; - - <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> - - &interaction.template.simple.rev; - - <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> - <listitem><para>Leading and trailing white space are - ignored.</para> - </listitem> - <listitem><para>Empty lines are skipped.</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> - <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> - <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> - <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> - <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> - -&interaction.template.svnstyle.syntax.input; - - <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> - - &interaction.template.svnstyle.syntax.error; - - <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> - <programlisting>___abort___: broken.style:1: parse error</programlisting> - </listitem> - <listitem><para>Next comes the name of the style file that - contains the error.</para> - <programlisting>abort: ___broken.style___:1: parse error</programlisting> - </listitem> - <listitem><para>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>Finally, a description of what went - wrong.</para> - <programlisting>abort: broken.style:1: ___parse error___</programlisting> - </listitem> - <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.</para> - - &interaction.template.svnstyle.id; - - <para>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.</para> - - &interaction.template.svnstyle.short; - - <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.</para> - - &interaction.template.svnstyle.template; - - <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><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>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.</para> - - &interaction.template.svnstyle.style; - - <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> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch12-mq-collab.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,518 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<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; + + <note> + <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 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 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 section <xref + linkend="sec.hgext.extdiff"/>.</para> + + </sect2> + </sect1> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->
--- a/en/ch12-mq.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1323 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<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>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 id="sec.mq.history"> - <title>The prehistory of Mercurial Queues</title> - - <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 id="sec.mq.quilt"> - <title>A patchwork quilt</title> - - <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 id="sec.mq.quilt-mq"> - <title>From patchwork quilt to Mercurial Queues</title> - - <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 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>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 below for a simple example of these commands - in action.</para> - -&interaction.mq.dodiff.diff; - - <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 id="sec.mq.start"> - <title>Getting started with Mercurial Queues</title> - - <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 - below.</para> - - <programlisting>[extensions] -hgext.mq =</programlisting> - - <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.</para> - -&interaction.mq.qinit-help.help; - - <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.</para> - -&interaction.mq.tutorial.qinit; - - <para>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>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>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>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 - to update the patch you are working on.</para> - -&interaction.mq.tutorial.qrefresh; - - <para>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>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>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>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>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>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> - - </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 - endterm="fig.mq.stack.caption" linkend="fig.mq.stack"/> illustrates - the difference between applied and tracked patches.</para> - - <informalfigure id="fig.mq.stack"> - <mediaobject> - <imageobject><imagedata fileref="images/mq-stack.png"/></imageobject> - <textobject><phrase>XXX add text</phrase></textobject> - <caption><para id="fig.mq.stack.caption">Applied and unapplied patches - in the MQ patch stack</para></caption> - </mediaobject> - </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 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>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>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 section <xref linkend="sec.mq.perf"/> - below.)</para> - -&interaction.mq.tutorial.qpush-a; - - </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. 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>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>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 id="sec.mq.adv-patch"> - <title>More about patches</title> - - <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 -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>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 id="sec.mq.perf"> - <title>Getting the best performance out of MQ</title> - - <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">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>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 id="sec.mq.merge"> - <title>Updating your patches when the underlying code - changes</title> - - <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">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>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">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>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 <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>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>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.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>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 - <literal role="tag">qbase</literal> and - <literal role="tag">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> - <programlisting>hg email qbase:qtip</programlisting> - <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> - <programlisting>hg log -r foo.patch:qtip subdir</programlisting> - </listitem> - </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> - - <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. 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>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 id="sec.mq.repo"> - <title>Managing patches in a repository</title> - - <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">hg - -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">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> (<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>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>alias mq=`hg -R $(hg root)/.hg/patches'</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">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>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> - -&interaction.mq.tools.tools; - - <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.</para> - - &interaction.mq.tarball.download; - - <para>Continue by creating a patch stack and making your - changes.</para> - - &interaction.mq.tarball.qinit; - - <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.</para> - - &interaction.mq.tarball.newsource; - - <para>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.</para> - - &interaction.mq.tarball.repush; - - </sect2> - <sect2 id="sec.mq.combine"> - <title>Combining entire patches</title> - - <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.</para> - - &interaction.mq.tools.lsdiff; - - <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> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch13-hgext.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,554 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.hgext"> + <?dbhtml filename="adding-functionality-with-extensions.html"?> + <title>Adding functionality with extensions</title> + + <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 id="sec.hgext.inotify"> + <title>Improve performance with the <literal + role="hg-ext">inotify</literal> extension</title> + + <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> + <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>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>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">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> 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> + <programlisting>python setup.py build --force +sudo python setup.py install --skip-build</programlisting> + </listitem> + </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>[extensions] inotify =</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 id="sec.hgext.extdiff"> + <title>Flexible diff support with the <literal + role="hg-ext">extdiff</literal> extension</title> + + <para>Mercurial's built-in <command role="hg-cmd">hg + diff</command> command outputs plaintext unified diffs.</para> + + &interaction.extdiff.diff; + + <para>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>[extensions] +extdiff =</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.</para> + + &interaction.extdiff.extdiff; + + <para>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">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>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>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>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>[extdiff] +cmd.kdiff3 =</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><literal>hg wibble</literal></quote>, which runs + <command>kdiff3</command>.</para> + <programlisting>[extdiff] + cmd.wibble = kdiff3</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>[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>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>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 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>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>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">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>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>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>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">hg -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">hg -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">hg -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">hg + --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">hg -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">hg -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">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: +-->
--- a/en/ch13-mq-collab.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,518 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.mq-collab"> - <?dbhtml filename="advanced-uses-of-mercurial-queues.html"?> - <title>Advanced uses of Mercurial Queues</title> - - <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> - - </sect2> - </sect1> - <sect1> - <title>Conditionally applying patches with guards</title> - - <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.</para> - - &interaction.mq.guards.init; - - <para>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> - - </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.</para> - - &interaction.mq.guards.qguard; - - <para>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>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; - - <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.)</para> - - &interaction.mq.guards.series; - - </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.</para> - - &interaction.mq.guards.qselect.foo; - - <para>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>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>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>Changing the selected guards changes the patches that are - applied.</para> - - &interaction.mq.guards.qselect.quux; - - <para>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>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 id="mq-collab.tips.interdiff"> - <title>Viewing the history of a patch</title> - - <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>[extensions] -extdiff =</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>hg extdiff -p hg-interdiff -r A:B my-change.patch</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>[extdiff] -cmd.interdiff = hg-interdiff</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>hg interdiff -r A:B my-change.patch</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> - -<!-- -local variables: -sgml-parent-document: ("00book.xml" "book" "chapter") -end: --->
--- a/en/ch14-hgext.xml Fri Mar 20 15:40:06 2009 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,554 +0,0 @@ -<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> - -<chapter id="chap.hgext"> - <?dbhtml filename="adding-functionality-with-extensions.html"?> - <title>Adding functionality with extensions</title> - - <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 id="sec.hgext.inotify"> - <title>Improve performance with the <literal - role="hg-ext">inotify</literal> extension</title> - - <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> - <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>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>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">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> 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> - <programlisting>python setup.py build --force -sudo python setup.py install --skip-build</programlisting> - </listitem> - </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>[extensions] inotify =</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 id="sec.hgext.extdiff"> - <title>Flexible diff support with the <literal - role="hg-ext">extdiff</literal> extension</title> - - <para>Mercurial's built-in <command role="hg-cmd">hg - diff</command> command outputs plaintext unified diffs.</para> - - &interaction.extdiff.diff; - - <para>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>[extensions] -extdiff =</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.</para> - - &interaction.extdiff.extdiff; - - <para>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">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>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>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>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>[extdiff] -cmd.kdiff3 =</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><literal>hg wibble</literal></quote>, which runs - <command>kdiff3</command>.</para> - <programlisting>[extdiff] - cmd.wibble = kdiff3</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>[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>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>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 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>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>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">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>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>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>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">hg -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">hg -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">hg -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">hg - --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">hg -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">hg -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">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: --->
--- a/po/zh.po Fri Mar 20 15:40:06 2009 +0800 +++ b/po/zh.po Fri Mar 20 16:43:35 2009 +0800 @@ -41,7 +41,7 @@ msgid "" msgstr "" "Project-Id-Version: hgbook 1.2\n" -"POT-Creation-Date: 2009-03-18 19:48+0800\n" +"POT-Creation-Date: 2009-03-20 15:47+0800\n" "PO-Revision-Date: 2009-03-18 19:50+0800\n" "Last-Translator: \n" "Language-Team: Simplified Chinese <i18n-zh@googlegroups.com >\n" @@ -475,15 +475,16 @@ "patches</command>." msgstr "" -#. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:42 -msgid "" -"\\subsection{<command role=\"hg-ext-mq\">qdelete</command>&emdash;delete a " -"patch from the <filename role=\"special\">series</filename> file}" -msgstr "" +#. type: Content of: <book><appendix><sect1><sect2><title> +#: ../en/appB-mq-ref.xml:43 +#, fuzzy +msgid "" +"<command role=\"hg-ext-mq\">qdelete</command>&emdash;delete a patch from the " +"<filename role=\"special\">series</filename> file}" +msgstr "<command role=\"hg-ext-mq\">qseries</command>&emdash;显示补丁序列" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:47 +#: ../en/appB-mq-ref.xml:48 msgid "" "The <command role=\"hg-ext-mq\">qdelete</command> command removes the entry " "for a patch from the <filename role=\"special\">series</filename> file in the " @@ -494,21 +495,21 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:56 ../en/appB-mq-ref.xml:98 ../en/appB-mq-ref.xml:156 -#: ../en/appB-mq-ref.xml:196 ../en/appB-mq-ref.xml:263 -#: ../en/appB-mq-ref.xml:334 ../en/appB-mq-ref.xml:403 -#: ../en/appB-mq-ref.xml:496 +#: ../en/appB-mq-ref.xml:57 ../en/appB-mq-ref.xml:99 ../en/appB-mq-ref.xml:157 +#: ../en/appB-mq-ref.xml:197 ../en/appB-mq-ref.xml:264 +#: ../en/appB-mq-ref.xml:335 ../en/appB-mq-ref.xml:404 +#: ../en/appB-mq-ref.xml:497 msgid "Options:" msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:58 +#: ../en/appB-mq-ref.xml:59 msgid "" "<option role=\"hg-ext-mq-cmd-qdel-opt\">-f</option>: Delete the patch file." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:65 +#: ../en/appB-mq-ref.xml:66 msgid "" "<command role=\"hg-ext-mq\">qdiff</command>&emdash;print a diff of the " "topmost applied patch" @@ -516,7 +517,7 @@ "<command role=\"hg-ext-mq\">qdiff</command>&emdash;显示最新应用补丁的差异" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:68 +#: ../en/appB-mq-ref.xml:69 msgid "" "The <command role=\"hg-ext-mq\">qdiff</command> command prints a diff of the " "topmost applied patch. It is equivalent to <command role=\"hg-cmd\">hg diff -" @@ -524,7 +525,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:74 +#: ../en/appB-mq-ref.xml:75 msgid "" "<command role=\"hg-ext-mq\">qfold</command>&emdash;merge (<quote>fold</" "quote>) several patches into one" @@ -533,7 +534,7 @@ "</quote>)成一个" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:77 +#: ../en/appB-mq-ref.xml:78 msgid "" "The <command role=\"hg-ext-mq\">qfold</command> command merges multiple " "patches into the topmost applied patch, so that the topmost applied patch " @@ -541,7 +542,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:82 +#: ../en/appB-mq-ref.xml:83 msgid "" "The patches to fold must not be applied; <command role=\"hg-ext-mq\">qfold</" "command> will exit with an error if any is. The order in which patches are " @@ -551,7 +552,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:90 +#: ../en/appB-mq-ref.xml:91 msgid "" "The comments from the folded patches are appended to the comments of the " "destination patch, with each block of comments separated by three asterisk " @@ -561,14 +562,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:100 +#: ../en/appB-mq-ref.xml:101 msgid "" "<option role=\"hg-ext-mq-cmd-qfold-opt\">-e</option>: Edit the commit message " "and patch description for the newly folded patch." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:105 +#: ../en/appB-mq-ref.xml:106 msgid "" "<option role=\"hg-ext-mq-cmd-qfold-opt\">-l</option>: Use the contents of the " "given file as the new commit message and patch description for the folded " @@ -576,21 +577,21 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:110 +#: ../en/appB-mq-ref.xml:111 msgid "" "<option role=\"hg-ext-mq-cmd-qfold-opt\">-m</option>: Use the given text as " "the new commit message and patch description for the folded patch." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:118 +#: ../en/appB-mq-ref.xml:119 msgid "" "<command role=\"hg-ext-mq\">qheader</command>&emdash;display the header/" "description of a patch" msgstr "<command role=\"hg-ext-mq\">qheader</command>&emdash;显示补丁头部描述" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:122 +#: ../en/appB-mq-ref.xml:123 msgid "" "The <command role=\"hg-ext-mq\">qheader</command> command prints the header, " "or description, of a patch. By default, it prints the header of the topmost " @@ -598,7 +599,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:129 +#: ../en/appB-mq-ref.xml:130 msgid "" "<command role=\"hg-ext-mq\">qimport</command>&emdash;import a third-party " "patch into the queue" @@ -606,7 +607,7 @@ "<command role=\"hg-ext-mq\">qimport</command>&emdash;将第三方补丁导入队列" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:132 +#: ../en/appB-mq-ref.xml:133 msgid "" "The <command role=\"hg-ext-mq\">qimport</command> command adds an entry for " "an external patch to the <filename role=\"special\">series</filename> file, " @@ -616,7 +617,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:140 +#: ../en/appB-mq-ref.xml:141 msgid "" "If the <filename role=\"special\" class=\"directory\">.hg/patches</filename> " "directory is a repository, <command role=\"hg-ext-mq\">qimport</command> " @@ -625,14 +626,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:148 +#: ../en/appB-mq-ref.xml:149 msgid "" "<command role=\"hg-ext-mq\">qinit</command>&emdash;prepare a repository to " "work with MQ" msgstr "<command role=\"hg-ext-mq\">qinit</command>&emdash;为使用 MQ 配置版本库" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:151 +#: ../en/appB-mq-ref.xml:152 msgid "" "The <command role=\"hg-ext-mq\">qinit</command> command prepares a repository " "to work with MQ. It creates a directory called <filename role=\"special\" " @@ -640,7 +641,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:158 +#: ../en/appB-mq-ref.xml:159 msgid "" "<option role=\"hg-ext-mq-cmd-qinit-opt\">-c</option>: Create <filename role=" "\"special\" class=\"directory\">.hg/patches</filename> as a repository in its " @@ -649,7 +650,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:168 +#: ../en/appB-mq-ref.xml:169 msgid "" "When the <filename role=\"special\" class=\"directory\">.hg/patches</" "filename> directory is a repository, the <command role=\"hg-ext-mq\">qimport</" @@ -658,12 +659,12 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:177 +#: ../en/appB-mq-ref.xml:178 msgid "<command role=\"hg-ext-mq\">qnew</command>&emdash;create a new patch" msgstr "<command role=\"hg-ext-mq\">qnew</command>&emdash;创建新补丁" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:180 +#: ../en/appB-mq-ref.xml:181 msgid "" "The <command role=\"hg-ext-mq\">qnew</command> command creates a new patch. " "It takes one mandatory argument, the name to use for the patch file. The " @@ -673,7 +674,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:188 +#: ../en/appB-mq-ref.xml:189 msgid "" "If <command role=\"hg-ext-mq\">qnew</command> finds modified files in the " "working directory, it will refuse to create a new patch unless the <option " @@ -683,7 +684,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:198 +#: ../en/appB-mq-ref.xml:199 msgid "" "<option role=\"hg-ext-mq-cmd-qnew-opt\">-f</option>: Create a new patch if " "the contents of the working directory are modified. Any outstanding " @@ -692,7 +693,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:205 +#: ../en/appB-mq-ref.xml:206 msgid "" "<option role=\"hg-ext-mq-cmd-qnew-opt\">-m</option>: Use the given text as " "the commit message. This text will be stored at the beginning of the patch " @@ -700,14 +701,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:214 +#: ../en/appB-mq-ref.xml:215 msgid "" "<command role=\"hg-ext-mq\">qnext</command>&emdash;print the name of the next " "patch" msgstr "<command role=\"hg-ext-mq\">qnext</command>&emdash;显示下个补丁的名称" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:217 +#: ../en/appB-mq-ref.xml:218 msgid "" "The <command role=\"hg-ext-mq\">qnext</command> command prints the name name " "of the next patch in the <filename role=\"special\">series</filename> file " @@ -716,13 +717,13 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:226 +#: ../en/appB-mq-ref.xml:227 msgid "" "<command role=\"hg-ext-mq\">qpop</command>&emdash;pop patches off the stack" msgstr "<command role=\"hg-ext-mq\">qpop</command>&emdash;删除堆栈顶部的补丁" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:229 +#: ../en/appB-mq-ref.xml:230 msgid "" "The <command role=\"hg-ext-mq\">qpop</command> command removes applied " "patches from the top of the stack of applied patches. By default, it removes " @@ -730,7 +731,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:233 +#: ../en/appB-mq-ref.xml:234 msgid "" "This command removes the changesets that represent the popped patches from " "the repository, and updates the working directory to undo the effects of the " @@ -738,7 +739,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:237 +#: ../en/appB-mq-ref.xml:238 msgid "" "This command takes an optional argument, which it uses as the name or index " "of the patch to pop to. If given a name, it will pop patches until the named " @@ -750,7 +751,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:247 +#: ../en/appB-mq-ref.xml:248 msgid "" "The <command role=\"hg-ext-mq\">qpop</command> command does not read or write " "patches or the <filename role=\"special\">series</filename> file. It is thus " @@ -761,7 +762,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:256 +#: ../en/appB-mq-ref.xml:257 msgid "" "By default, the <command role=\"hg-ext-mq\">qpop</command> command will not " "pop any patches if the working directory has been modified. You can override " @@ -770,7 +771,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:265 +#: ../en/appB-mq-ref.xml:266 msgid "" "<option role=\"hg-ext-mq-cmd-qpop-opt\">-a</option>: Pop all applied " "patches. This returns the repository to its state before you applied any " @@ -778,21 +779,21 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:270 +#: ../en/appB-mq-ref.xml:271 msgid "" "<option role=\"hg-ext-mq-cmd-qpop-opt\">-f</option>: Forcibly revert any " "modifications to the working directory when popping." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:275 +#: ../en/appB-mq-ref.xml:276 msgid "" "<option role=\"hg-ext-mq-cmd-qpop-opt\">-n</option>: Pop a patch from the " "named queue." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:280 +#: ../en/appB-mq-ref.xml:281 msgid "" "The <command role=\"hg-ext-mq\">qpop</command> command removes one line from " "the end of the <filename role=\"special\">status</filename> file for each " @@ -800,14 +801,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:287 +#: ../en/appB-mq-ref.xml:288 msgid "" "<command role=\"hg-ext-mq\">qprev</command>&emdash;print the name of the " "previous patch" msgstr "<command role=\"hg-ext-mq\">qprev</command>&emdash;显示上个补丁的名称" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:290 +#: ../en/appB-mq-ref.xml:291 msgid "" "The <command role=\"hg-ext-mq\">qprev</command> command prints the name of " "the patch in the <filename role=\"special\">series</filename> file that comes " @@ -816,32 +817,32 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:299 +#: ../en/appB-mq-ref.xml:300 msgid "" "<command role=\"hg-ext-mq\">qpush</command>&emdash;push patches onto the stack" msgstr "<command role=\"hg-ext-mq\">qpush</command>&emdash;增加补丁到堆栈" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:302 +#: ../en/appB-mq-ref.xml:303 msgid "" "The <command role=\"hg-ext-mq\">qpush</command> command adds patches onto the " "applied stack. By default, it adds only one patch." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:306 +#: ../en/appB-mq-ref.xml:307 msgid "" "This command creates a new changeset to represent each applied patch, and " "updates the working directory to apply the effects of the patches." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:310 +#: ../en/appB-mq-ref.xml:311 msgid "The default data used when creating a changeset are as follows:" msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:313 +#: ../en/appB-mq-ref.xml:314 msgid "" "The commit date and time zone are the current date and time zone. Because " "these data are used to compute the identity of a changeset, this means that " @@ -851,14 +852,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:321 +#: ../en/appB-mq-ref.xml:322 msgid "" "The author is the same as the default used by the <command role=\"hg-cmd\">hg " "commit</command> command." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:325 +#: ../en/appB-mq-ref.xml:326 msgid "" "The commit message is any text from the patch file that comes before the " "first diff header. If there is no such text, a default commit message is " @@ -866,14 +867,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:330 +#: ../en/appB-mq-ref.xml:331 msgid "" "If a patch contains a Mercurial patch header (XXX add link), the information " "in the patch header overrides these defaults." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:336 +#: ../en/appB-mq-ref.xml:337 msgid "" "<option role=\"hg-ext-mq-cmd-qpush-opt\">-a</option>: Push all unapplied " "patches from the <filename role=\"special\">series</filename> file until " @@ -881,14 +882,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:342 +#: ../en/appB-mq-ref.xml:343 msgid "" "<option role=\"hg-ext-mq-cmd-qpush-opt\">-l</option>: Add the name of the " "patch to the end of the commit message." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:346 +#: ../en/appB-mq-ref.xml:347 msgid "" "<option role=\"hg-ext-mq-cmd-qpush-opt\">-m</option>: If a patch fails to " "apply cleanly, use the entry for the patch in another saved queue to compute " @@ -898,14 +899,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:354 +#: ../en/appB-mq-ref.xml:355 msgid "" "<option role=\"hg-ext-mq-cmd-qpush-opt\">-n</option>: Use the named queue if " "merging while pushing." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:359 +#: ../en/appB-mq-ref.xml:360 msgid "" "The <command role=\"hg-ext-mq\">qpush</command> command reads, but does not " "modify, the <filename role=\"special\">series</filename> file. It appends " @@ -914,14 +915,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:367 +#: ../en/appB-mq-ref.xml:368 msgid "" "<command role=\"hg-ext-mq\">qrefresh</command>&emdash;update the topmost " "applied patch" msgstr "<command role=\"hg-ext-mq\">qrefresh</command>&emdash;更新最新的补丁" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:371 +#: ../en/appB-mq-ref.xml:372 msgid "" "The <command role=\"hg-ext-mq\">qrefresh</command> command updates the " "topmost applied patch. It modifies the patch, removes the old changeset that " @@ -930,28 +931,28 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:377 +#: ../en/appB-mq-ref.xml:378 msgid "" "The <command role=\"hg-ext-mq\">qrefresh</command> command looks for the " "following modifications:" msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:380 +#: ../en/appB-mq-ref.xml:381 msgid "" "Changes to the commit message, i.e. the text before the first diff header in " "the patch file, are reflected in the new changeset that represents the patch." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:385 +#: ../en/appB-mq-ref.xml:386 msgid "" "Modifications to tracked files in the working directory are added to the " "patch." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:388 +#: ../en/appB-mq-ref.xml:389 msgid "" "Changes to the files tracked using <command role=\"hg-cmd\">hg add</command>, " "<command role=\"hg-cmd\">hg copy</command>, <command role=\"hg-cmd\">hg " @@ -961,7 +962,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:397 +#: ../en/appB-mq-ref.xml:398 msgid "" "Even if <command role=\"hg-ext-mq\">qrefresh</command> detects no changes, it " "still recreates the changeset that represents the patch. This causes the " @@ -970,33 +971,33 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:405 +#: ../en/appB-mq-ref.xml:406 msgid "" "<option role=\"hg-ext-mq-cmd-qrefresh-opt\">-e</option>: Modify the commit " "and patch description, using the preferred text editor." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:410 +#: ../en/appB-mq-ref.xml:411 msgid "" "<option role=\"hg-ext-mq-cmd-qrefresh-opt\">-m</option>: Modify the commit " "message and patch description, using the given text." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:415 +#: ../en/appB-mq-ref.xml:416 msgid "" "<option role=\"hg-ext-mq-cmd-qrefresh-opt\">-l</option>: Modify the commit " "message and patch description, using text from the given file." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:423 +#: ../en/appB-mq-ref.xml:424 msgid "<command role=\"hg-ext-mq\">qrename</command>&emdash;rename a patch" msgstr "<command role=\"hg-ext-mq\">qrename</command>&emdash;改名补丁" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:426 +#: ../en/appB-mq-ref.xml:427 msgid "" "The <command role=\"hg-ext-mq\">qrename</command> command renames a patch, " "and changes the entry for the patch in the <filename role=\"special\">series</" @@ -1004,7 +1005,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:430 +#: ../en/appB-mq-ref.xml:431 msgid "" "With a single argument, <command role=\"hg-ext-mq\">qrename</command> renames " "the topmost applied patch. With two arguments, it renames its first argument " @@ -1012,37 +1013,37 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:437 +#: ../en/appB-mq-ref.xml:438 msgid "" "<command role=\"hg-ext-mq\">qrestore</command>&emdash;restore saved queue " "state" msgstr "<command role=\"hg-ext-mq\">qrestore</command>&emdash;恢复保存的队列" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:441 +#: ../en/appB-mq-ref.xml:442 msgid "XXX No idea what this does." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:445 +#: ../en/appB-mq-ref.xml:446 msgid "" "<command role=\"hg-ext-mq\">qsave</command>&emdash;save current queue state" msgstr "<command role=\"hg-ext-mq\">qsave</command>&emdash;保存当前的队列状态" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:448 +#: ../en/appB-mq-ref.xml:449 msgid "XXX Likewise." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:452 +#: ../en/appB-mq-ref.xml:453 msgid "" "<command role=\"hg-ext-mq\">qseries</command>&emdash;print the entire patch " "series" msgstr "<command role=\"hg-ext-mq\">qseries</command>&emdash;显示补丁序列" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:455 +#: ../en/appB-mq-ref.xml:456 msgid "" "The <command role=\"hg-ext-mq\">qseries</command> command prints the entire " "patch series from the <filename role=\"special\">series</filename> file. It " @@ -1051,21 +1052,21 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:463 +#: ../en/appB-mq-ref.xml:464 msgid "" "<command role=\"hg-ext-mq\">qtop</command>&emdash;print the name of the " "current patch" msgstr "<command role=\"hg-ext-mq\">qtop</command>&emdash;显示当前补丁的名称" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:466 +#: ../en/appB-mq-ref.xml:467 msgid "" "The <command role=\"hg-ext-mq\">qtop</command> prints the name of the topmost " "currently applied patch." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:471 +#: ../en/appB-mq-ref.xml:472 msgid "" "<command role=\"hg-ext-mq\">qunapplied</command>&emdash;print patches not yet " "applied" @@ -1073,7 +1074,7 @@ "<command role=\"hg-ext-mq\">qunapplied</command>&emdash;显示尚未应用的补丁" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:475 +#: ../en/appB-mq-ref.xml:476 msgid "" "The <command role=\"hg-ext-mq\">qunapplied</command> command prints the names " "of patches from the <filename role=\"special\">series</filename> file that " @@ -1082,14 +1083,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:483 +#: ../en/appB-mq-ref.xml:484 msgid "" "<command role=\"hg-cmd\">hg strip</command>&emdash;remove a revision and " "descendants" msgstr "<command role=\"hg-cmd\">hg strip</command>&emdash;删除一个版本及其后继" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:486 +#: ../en/appB-mq-ref.xml:487 msgid "" "The <command role=\"hg-cmd\">hg strip</command> command removes a revision, " "and all of its descendants, from the repository. It undoes the effects of " @@ -1098,7 +1099,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:492 +#: ../en/appB-mq-ref.xml:493 msgid "" "The <command role=\"hg-cmd\">hg strip</command> command saves a backup of the " "removed changesets in a bundle, so that they can be reapplied if removed in " @@ -1106,14 +1107,14 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:498 +#: ../en/appB-mq-ref.xml:499 msgid "" "<option role=\"hg-opt-strip\">-b</option>: Save unrelated changesets that are " "intermixed with the stripped changesets in the backup bundle." msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:502 +#: ../en/appB-mq-ref.xml:503 msgid "" "<option role=\"hg-opt-strip\">-f</option>: If a branch has multiple heads, " "remove all heads. XXX This should be renamed, and use <literal>-f</literal> " @@ -1121,22 +1122,22 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><itemizedlist><listitem><para> -#: ../en/appB-mq-ref.xml:507 +#: ../en/appB-mq-ref.xml:508 msgid "<option role=\"hg-opt-strip\">-n</option>: Do not save a backup bundle." msgstr "" #. type: Content of: <book><appendix><sect1><title> -#: ../en/appB-mq-ref.xml:514 +#: ../en/appB-mq-ref.xml:515 msgid "MQ file reference" msgstr "MQ 文件参考" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:517 +#: ../en/appB-mq-ref.xml:518 msgid "The <filename role=\"special\">series</filename> file" msgstr "<filename role=\"special\">序列</filename>文件" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:520 +#: ../en/appB-mq-ref.xml:521 msgid "" "The <filename role=\"special\">series</filename> file contains a list of the " "names of all patches that MQ can apply. It is represented as a list of " @@ -1145,7 +1146,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:526 +#: ../en/appB-mq-ref.xml:527 msgid "" "Lines may contain comments. A comment begins with the <quote><literal>#</" "literal></quote> character, and extends to the end of the line. Empty lines, " @@ -1153,7 +1154,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:531 +#: ../en/appB-mq-ref.xml:532 msgid "" "You will often need to edit the <filename role=\"special\">series</filename> " "file by hand, hence the support for comments and empty lines noted above. " @@ -1164,7 +1165,7 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:540 +#: ../en/appB-mq-ref.xml:541 msgid "" "Placing the <filename role=\"special\">series</filename> file under revision " "control is also supported; it is a good idea to place all of the patches that " @@ -1175,12 +1176,12 @@ msgstr "" #. type: Content of: <book><appendix><sect1><sect2><title> -#: ../en/appB-mq-ref.xml:550 +#: ../en/appB-mq-ref.xml:551 msgid "The <filename role=\"special\">status</filename> file" msgstr "<filename role=\"special\">状态</filename>文件" #. type: Content of: <book><appendix><sect1><sect2><para> -#: ../en/appB-mq-ref.xml:553 +#: ../en/appB-mq-ref.xml:554 msgid "" "The <filename role=\"special\">status</filename> file contains the names and " "changeset hashes of all patches that MQ currently has applied. Unlike the " @@ -1519,941 +1520,17 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch01-intro.xml:5 -msgid "Introduction" -msgstr "简介" - -#. type: Content of: <book><chapter><sect1><title> -#: ../en/ch01-intro.xml:8 -msgid "About revision control" -msgstr "关于版本控制" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:10 -msgid "" -"Revision control is the process of managing multiple versions of a piece of " -"information. In its simplest form, this is something that many people do by " -"hand: every time you modify a file, save it under a new name that contains a " -"number, each one higher than the number of the preceding version." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:16 -msgid "" -"Manually managing multiple versions of even a single file is an error-prone " -"task, though, so software tools to help automate this process have long been " -"available. The earliest automated revision control tools were intended to " -"help a single user to manage revisions of a single file. Over the past few " -"decades, the scope of revision control tools has expanded greatly; they now " -"manage multiple files, and help multiple people to work together. The best " -"modern revision control tools have no problem coping with thousands of people " -"working together on projects that consist of hundreds of thousands of files." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch01-intro.xml:28 -msgid "Why use revision control?" -msgstr "为什么使用版本控制?" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:30 -msgid "" -"There are a number of reasons why you or your team might want to use an " -"automated revision control tool for a project." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:34 -msgid "" -"It will track the history and evolution of your project, so you don't have " -"to. For every change, you'll have a log of <emphasis>who</emphasis> made it; " -"<emphasis>why</emphasis> they made it; <emphasis>when</emphasis> they made " -"it; and <emphasis>what</emphasis> the change was." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:41 -msgid "" -"When you're working with other people, revision control software makes it " -"easier for you to collaborate. For example, when people more or less " -"simultaneously make potentially incompatible changes, the software will help " -"you to identify and resolve those conflicts." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:47 -msgid "" -"It can help you to recover from mistakes. If you make a change that later " -"turns out to be in error, you can revert to an earlier version of one or more " -"files. In fact, a <emphasis>really</emphasis> good revision control tool " -"will even help you to efficiently figure out exactly when a problem was " -"introduced (see section <xref linkend=\"sec.undo.bisect\"/> for details)." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:54 -msgid "" -"It will help you to work simultaneously on, and manage the drift between, " -"multiple versions of your project." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:57 -msgid "" -"Most of these reasons are equally valid---at least in theory---whether you're " -"working on a project by yourself, or with a hundred other people." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:61 -msgid "" -"A key question about the practicality of revision control at these two " -"different scales (<quote>lone hacker</quote> and <quote>huge team</quote>) is " -"how its <emphasis>benefits</emphasis> compare to its <emphasis>costs</" -"emphasis>. A revision control tool that's difficult to understand or use is " -"going to impose a high cost." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:69 -msgid "" -"A five-hundred-person project is likely to collapse under its own weight " -"almost immediately without a revision control tool and process. In this case, " -"the cost of using revision control might hardly seem worth considering, since " -"<emphasis>without</emphasis> it, failure is almost guaranteed." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:76 -msgid "" -"On the other hand, a one-person <quote>quick hack</quote> might seem like a " -"poor place to use a revision control tool, because surely the cost of using " -"one must be close to the overall cost of the project. Right?" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:81 -msgid "" -"Mercurial uniquely supports <emphasis>both</emphasis> of these scales of " -"development. You can learn the basics in just a few minutes, and due to its " -"low overhead, you can apply revision control to the smallest of projects with " -"ease. Its simplicity means you won't have a lot of abstruse concepts or " -"command sequences competing for mental space with whatever you're " -"<emphasis>really</emphasis> trying to do. At the same time, Mercurial's high " -"performance and peer-to-peer nature let you scale painlessly to handle large " -"projects." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:91 -msgid "" -"No revision control tool can rescue a poorly run project, but a good choice " -"of tools can make a huge difference to the fluidity with which you can work " -"on a project." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch01-intro.xml:97 -msgid "The many names of revision control" -msgstr "版本控制的别名" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:99 -msgid "" -"Revision control is a diverse field, so much so that it doesn't actually have " -"a single name or acronym. Here are a few of the more common names and " -"acronyms you'll encounter:" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:104 -msgid "Revision control (RCS)" -msgstr "版本控制(RCS)" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:105 -msgid "Software configuration management (SCM), or configuration management" -msgstr "软件配置管理(SCM),或配置管理" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:107 -msgid "Source code management" -msgstr "源代码管理" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:108 -msgid "Source code control, or source control" -msgstr "源代码控制,或源控制" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:110 -msgid "Version control (VCS)" -msgstr "版本控制(VCS)" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:112 -msgid "" -"Some people claim that these terms actually have different meanings, but in " -"practice they overlap so much that there's no agreed or even useful way to " -"tease them apart." -msgstr "" - -#. type: Content of: <book><chapter><sect1><title> -#: ../en/ch01-intro.xml:119 -msgid "A short history of revision control" -msgstr "版本控制简史" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:121 -msgid "" -"The best known of the old-time revision control tools is SCCS (Source Code " -"Control System), which Marc Rochkind wrote at Bell Labs, in the early 1970s. " -"SCCS operated on individual files, and required every person working on a " -"project to have access to a shared workspace on a single system. Only one " -"person could modify a file at any time; arbitration for access to files was " -"via locks. It was common for people to lock files, and later forget to " -"unlock them, preventing anyone else from modifying those files without the " -"help of an administrator." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:132 -msgid "" -"Walter Tichy developed a free alternative to SCCS in the early 1980s; he " -"called his program RCS (Revision Control System). Like SCCS, RCS required " -"developers to work in a single shared workspace, and to lock files to prevent " -"multiple people from modifying them simultaneously." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:138 -msgid "" -"Later in the 1980s, Dick Grune used RCS as a building block for a set of " -"shell scripts he initially called cmt, but then renamed to CVS (Concurrent " -"Versions System). The big innovation of CVS was that it let developers work " -"simultaneously and somewhat independently in their own personal workspaces. " -"The personal workspaces prevented developers from stepping on each other's " -"toes all the time, as was common with SCCS and RCS. Each developer had a copy " -"of every project file, and could modify their copies independently. They had " -"to merge their edits prior to committing changes to the central repository." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:149 -msgid "" -"Brian Berliner took Grune's original scripts and rewrote them in C, releasing " -"in 1989 the code that has since developed into the modern version of CVS. " -"CVS subsequently acquired the ability to operate over a network connection, " -"giving it a client/server architecture. CVS's architecture is centralised; " -"only the server has a copy of the history of the project. Client workspaces " -"just contain copies of recent versions of the project's files, and a little " -"metadata to tell them where the server is. CVS has been enormously " -"successful; it is probably the world's most widely used revision control " -"system." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:160 -msgid "" -"In the early 1990s, Sun Microsystems developed an early distributed revision " -"control system, called TeamWare. A TeamWare workspace contains a complete " -"copy of the project's history. TeamWare has no notion of a central " -"repository. (CVS relied upon RCS for its history storage; TeamWare used " -"SCCS.)" -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:167 -msgid "" -"As the 1990s progressed, awareness grew of a number of problems with CVS. It " -"records simultaneous changes to multiple files individually, instead of " -"grouping them together as a single logically atomic operation. It does not " -"manage its file hierarchy well; it is easy to make a mess of a repository by " -"renaming files and directories. Worse, its source code is difficult to read " -"and maintain, which made the <quote>pain level</quote> of fixing these " -"architectural problems prohibitive." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:177 -msgid "" -"In 2001, Jim Blandy and Karl Fogel, two developers who had worked on CVS, " -"started a project to replace it with a tool that would have a better " -"architecture and cleaner code. The result, Subversion, does not stray from " -"CVS's centralised client/server model, but it adds multi-file atomic commits, " -"better namespace management, and a number of other features that make it a " -"generally better tool than CVS. Since its initial release, it has rapidly " -"grown in popularity." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:186 -msgid "" -"More or less simultaneously, Graydon Hoare began working on an ambitious " -"distributed revision control system that he named Monotone. While Monotone " -"addresses many of CVS's design flaws and has a peer-to-peer architecture, it " -"goes beyond earlier (and subsequent) revision control tools in a number of " -"innovative ways. It uses cryptographic hashes as identifiers, and has an " -"integral notion of <quote>trust</quote> for code from different sources." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:195 -msgid "" -"Mercurial began life in 2005. While a few aspects of its design are " -"influenced by Monotone, Mercurial focuses on ease of use, high performance, " -"and scalability to very large projects." -msgstr "" - -#. type: Content of: <book><chapter><sect1><title> -#: ../en/ch01-intro.xml:202 -msgid "Trends in revision control" -msgstr "版本控制的发展趋势" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:204 -msgid "" -"There has been an unmistakable trend in the development and use of revision " -"control tools over the past four decades, as people have become familiar with " -"the capabilities of their tools and constrained by their limitations." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:209 -msgid "" -"The first generation began by managing single files on individual computers. " -"Although these tools represented a huge advance over ad-hoc manual revision " -"control, their locking model and reliance on a single computer limited them " -"to small, tightly-knit teams." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:215 -msgid "" -"The second generation loosened these constraints by moving to network-" -"centered architectures, and managing entire projects at a time. As projects " -"grew larger, they ran into new problems. With clients needing to talk to " -"servers very frequently, server scaling became an issue for large projects. " -"An unreliable network connection could prevent remote users from being able " -"to talk to the server at all. As open source projects started making read-" -"only access available anonymously to anyone, people without commit privileges " -"found that they could not use the tools to interact with a project in a " -"natural way, as they could not record their changes." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:227 -msgid "" -"The current generation of revision control tools is peer-to-peer in nature. " -"All of these systems have dropped the dependency on a single central server, " -"and allow people to distribute their revision control data to where it's " -"actually needed. Collaboration over the Internet has moved from constrained " -"by technology to a matter of choice and consensus. Modern tools can operate " -"offline indefinitely and autonomously, with a network connection only needed " -"when syncing changes with another repository." -msgstr "" - -#. type: Content of: <book><chapter><sect1><title> -#: ../en/ch01-intro.xml:239 -msgid "A few of the advantages of distributed revision control" -msgstr "分布版本控制的优点" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:242 -msgid "" -"Even though distributed revision control tools have for several years been as " -"robust and usable as their previous-generation counterparts, people using " -"older tools have not yet necessarily woken up to their advantages. There are " -"a number of ways in which distributed tools shine relative to centralised " -"ones." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:249 -msgid "" -"For an individual developer, distributed tools are almost always much faster " -"than centralised tools. This is for a simple reason: a centralised tool " -"needs to talk over the network for many common operations, because most " -"metadata is stored in a single copy on the central server. A distributed " -"tool stores all of its metadata locally. All else being equal, talking over " -"the network adds overhead to a centralised tool. Don't underestimate the " -"value of a snappy, responsive tool: you're going to spend a lot of time " -"interacting with your revision control software." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:260 -msgid "" -"Distributed tools are indifferent to the vagaries of your server " -"infrastructure, again because they replicate metadata to so many locations. " -"If you use a centralised system and your server catches fire, you'd better " -"hope that your backup media are reliable, and that your last backup was " -"recent and actually worked. With a distributed tool, you have many backups " -"available on every contributor's computer." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:268 -msgid "" -"The reliability of your network will affect distributed tools far less than " -"it will centralised tools. You can't even use a centralised tool without a " -"network connection, except for a few highly constrained commands. With a " -"distributed tool, if your network connection goes down while you're working, " -"you may not even notice. The only thing you won't be able to do is talk to " -"repositories on other computers, something that is relatively rare compared " -"with local operations. If you have a far-flung team of collaborators, this " -"may be significant." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch01-intro.xml:279 -msgid "Advantages for open source projects" -msgstr "开源项目的优点" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:281 -msgid "" -"If you take a shine to an open source project and decide that you would like " -"to start hacking on it, and that project uses a distributed revision control " -"tool, you are at once a peer with the people who consider themselves the " -"<quote>core</quote> of that project. If they publish their repositories, you " -"can immediately copy their project history, start making changes, and record " -"your work, using the same tools in the same ways as insiders. By contrast, " -"with a centralised tool, you must use the software in a <quote>read only</" -"quote> mode unless someone grants you permission to commit changes to their " -"central server. Until then, you won't be able to record changes, and your " -"local modifications will be at risk of corruption any time you try to update " -"your client's view of the repository." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch01-intro.xml:297 -msgid "The forking non-problem" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch01-intro.xml:299 -msgid "" -"It has been suggested that distributed revision control tools pose some sort " -"of risk to open source projects because they make it easy to <quote>fork</" -"quote> the development of a project. A fork happens when there are " -"differences in opinion or attitude between groups of developers that cause " -"them to decide that they can't work together any longer. Each side takes a " -"more or less complete copy of the project's source code, and goes off in its " -"own direction." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch01-intro.xml:309 -msgid "" -"Sometimes the camps in a fork decide to reconcile their differences. With a " -"centralised revision control system, the <emphasis>technical</emphasis> " -"process of reconciliation is painful, and has to be performed largely by " -"hand. You have to decide whose revision history is going to <quote>win</" -"quote>, and graft the other team's changes into the tree somehow. This " -"usually loses some or all of one side's revision history." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch01-intro.xml:318 -msgid "" -"What distributed tools do with respect to forking is they make forking the " -"<emphasis>only</emphasis> way to develop a project. Every single change that " -"you make is potentially a fork point. The great strength of this approach is " -"that a distributed revision control tool has to be really good at " -"<emphasis>merging</emphasis> forks, because forks are absolutely fundamental: " -"they happen all the time." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch01-intro.xml:327 -msgid "" -"If every piece of work that everybody does, all the time, is framed in terms " -"of forking and merging, then what the open source world refers to as a " -"<quote>fork</quote> becomes <emphasis>purely</emphasis> a social issue. If " -"anything, distributed tools <emphasis>lower</emphasis> the likelihood of a " -"fork:" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:334 -msgid "" -"They eliminate the social distinction that centralised tools impose: that " -"between insiders (people with commit access) and outsiders (people without)." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:338 -msgid "" -"They make it easier to reconcile after a social fork, because all that's " -"involved from the perspective of the revision control software is just " -"another merge." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch01-intro.xml:343 -msgid "" -"Some people resist distributed tools because they want to retain tight " -"control over their projects, and they believe that centralised tools give " -"them this control. However, if you're of this belief, and you publish your " -"CVS or Subversion repositories publicly, there are plenty of tools available " -"that can pull out your entire project's history (albeit slowly) and recreate " -"it somewhere that you don't control. So while your control in this case is " -"illusory, you are forgoing the ability to fluidly collaborate with whatever " -"people feel compelled to mirror and fork your history." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch01-intro.xml:358 -msgid "Advantages for commercial projects" -msgstr "商业项目的优点" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:360 -msgid "" -"Many commercial projects are undertaken by teams that are scattered across " -"the globe. Contributors who are far from a central server will see slower " -"command execution and perhaps less reliability. Commercial revision control " -"systems attempt to ameliorate these problems with remote-site replication add-" -"ons that are typically expensive to buy and cantankerous to administer. A " -"distributed system doesn't suffer from these problems in the first place. " -"Better yet, you can easily set up multiple authoritative servers, say one per " -"site, so that there's no redundant communication between repositories over " -"expensive long-haul network links." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:372 -msgid "" -"Centralised revision control systems tend to have relatively low " -"scalability. It's not unusual for an expensive centralised system to fall " -"over under the combined load of just a few dozen concurrent users. Once " -"again, the typical response tends to be an expensive and clunky replication " -"facility. Since the load on a central server---if you have one at all---is " -"many times lower with a distributed tool (because all of the data is " -"replicated everywhere), a single cheap server can handle the needs of a much " -"larger team, and replication to balance load becomes a simple matter of " -"scripting." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:384 -msgid "" -"If you have an employee in the field, troubleshooting a problem at a " -"customer's site, they'll benefit from distributed revision control. The tool " -"will let them generate custom builds, try different fixes in isolation from " -"each other, and search efficiently through history for the sources of bugs " -"and regressions in the customer's environment, all without needing to connect " -"to your company's network." -msgstr "" - -#. type: Content of: <book><chapter><sect1><title> -#: ../en/ch01-intro.xml:395 -msgid "Why choose Mercurial?" -msgstr "为什么选择 Mercurial?" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:397 -msgid "" -"Mercurial has a unique set of properties that make it a particularly good " -"choice as a revision control system." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:400 -msgid "It is easy to learn and use." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:401 -msgid "It is lightweight." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:402 -msgid "It scales excellently." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:403 -msgid "It is easy to customise." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:406 -msgid "" -"If you are at all familiar with revision control systems, you should be able " -"to get up and running with Mercurial in less than five minutes. Even if not, " -"it will take no more than a few minutes longer. Mercurial's command and " -"feature sets are generally uniform and consistent, so you can keep track of a " -"few general rules instead of a host of exceptions." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:413 -msgid "" -"On a small project, you can start working with Mercurial in moments. Creating " -"new changes and branches; transferring changes around (whether locally or " -"over a network); and history and status operations are all fast. Mercurial " -"attempts to stay nimble and largely out of your way by combining low " -"cognitive overhead with blazingly fast operations." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:420 -msgid "" -"The usefulness of Mercurial is not limited to small projects: it is used by " -"projects with hundreds to thousands of contributors, each containing tens of " -"thousands of files and hundreds of megabytes of source code." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:425 -msgid "" -"If the core functionality of Mercurial is not enough for you, it's easy to " -"build on. Mercurial is well suited to scripting tasks, and its clean " -"internals and implementation in Python make it easy to add features in the " -"form of extensions. There are a number of popular and useful extensions " -"already available, ranging from helping to identify bugs to improving " -"performance." -msgstr "" - -#. type: Content of: <book><chapter><sect1><title> -#: ../en/ch01-intro.xml:435 -msgid "Mercurial compared with other tools" -msgstr "Mercurial 与其它工具的比较" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:437 -msgid "" -"Before you read on, please understand that this section necessarily reflects " -"my own experiences, interests, and (dare I say it) biases. I have used every " -"one of the revision control tools listed below, in most cases for several " -"years at a time." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:445 ../en/ch01-intro.xml:656 -msgid "Subversion" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:447 -msgid "" -"Subversion is a popular revision control tool, developed to replace CVS. It " -"has a centralised client/server architecture." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:451 -msgid "" -"Subversion and Mercurial have similarly named commands for performing the " -"same operations, so if you're familiar with one, it is easy to learn to use " -"the other. Both tools are portable to all popular operating systems." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:456 -msgid "" -"Prior to version 1.5, Subversion had no useful support for merges. At the " -"time of writing, its merge tracking capability is new, and known to be <ulink " -"url=\"http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced." -"html#svn.branchmerge.advanced.finalword\">complicated and buggy</ulink>." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:462 -msgid "" -"Mercurial has a substantial performance advantage over Subversion on every " -"revision control operation I have benchmarked. I have measured its advantage " -"as ranging from a factor of two to a factor of six when compared with " -"Subversion 1.4.3's <emphasis>ra_local</emphasis> file store, which is the " -"fastest access method available. In more realistic deployments involving a " -"network-based store, Subversion will be at a substantially larger " -"disadvantage. Because many Subversion commands must talk to the server and " -"Subversion does not have useful replication facilities, server capacity and " -"network bandwidth become bottlenecks for modestly large projects." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:475 -msgid "" -"Additionally, Subversion incurs substantial storage overhead to avoid network " -"transactions for a few common operations, such as finding modified files " -"(<literal>status</literal>) and displaying modifications against the current " -"revision (<literal>diff</literal>). As a result, a Subversion working copy " -"is often the same size as, or larger than, a Mercurial repository and working " -"directory, even though the Mercurial repository contains a complete history " -"of the project." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:485 -msgid "" -"Subversion is widely supported by third party tools. Mercurial currently " -"lags considerably in this area. This gap is closing, however, and indeed " -"some of Mercurial's GUI tools now outshine their Subversion equivalents. " -"Like Mercurial, Subversion has an excellent user manual." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:491 -msgid "" -"Because Subversion doesn't store revision history on the client, it is well " -"suited to managing projects that deal with lots of large, opaque binary " -"files. If you check in fifty revisions to an incompressible 10MB file, " -"Subversion's client-side space usage stays constant The space used by any " -"distributed SCM will grow rapidly in proportion to the number of revisions, " -"because the differences between each revision are large." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:500 -msgid "" -"In addition, it's often difficult or, more usually, impossible to merge " -"different versions of a binary file. Subversion's ability to let a user lock " -"a file, so that they temporarily have the exclusive right to commit changes " -"to it, can be a significant advantage to a project where binary files are " -"widely used." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:507 -msgid "" -"Mercurial can import revision history from a Subversion repository. It can " -"also export revision history to a Subversion repository. This makes it easy " -"to <quote>test the waters</quote> and use Mercurial and Subversion in " -"parallel before deciding to switch. History conversion is incremental, so " -"you can perform an initial conversion, then small additional conversions " -"afterwards to bring in new changes." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:519 ../en/ch01-intro.xml:658 -msgid "Git" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:521 -msgid "" -"Git is a distributed revision control tool that was developed for managing " -"the Linux kernel source tree. Like Mercurial, its early design was somewhat " -"influenced by Monotone." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:526 -msgid "" -"Git has a very large command set, with version 1.5.0 providing 139 individual " -"commands. It has something of a reputation for being difficult to learn. " -"Compared to Git, Mercurial has a strong focus on simplicity." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:531 -msgid "" -"In terms of performance, Git is extremely fast. In several cases, it is " -"faster than Mercurial, at least on Linux, while Mercurial performs better on " -"other operations. However, on Windows, the performance and general level of " -"support that Git provides is, at the time of writing, far behind that of " -"Mercurial." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:538 -msgid "" -"While a Mercurial repository needs no maintenance, a Git repository requires " -"frequent manual <quote>repacks</quote> of its metadata. Without these, " -"performance degrades, while space usage grows rapidly. A server that " -"contains many Git repositories that are not rigorously and frequently " -"repacked will become heavily disk-bound during backups, and there have been " -"instances of daily backups taking far longer than 24 hours as a result. A " -"freshly packed Git repository is slightly smaller than a Mercurial " -"repository, but an unpacked repository is several orders of magnitude larger." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:549 -msgid "" -"The core of Git is written in C. Many Git commands are implemented as shell " -"or Perl scripts, and the quality of these scripts varies widely. I have " -"encountered several instances where scripts charged along blindly in the " -"presence of errors that should have been fatal." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:555 -msgid "Mercurial can import revision history from a Git repository." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:561 ../en/ch01-intro.xml:657 -msgid "CVS" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:563 -msgid "" -"CVS is probably the most widely used revision control tool in the world. Due " -"to its age and internal untidiness, it has been only lightly maintained for " -"many years." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:567 -msgid "" -"It has a centralised client/server architecture. It does not group related " -"file changes into atomic commits, making it easy for people to <quote>break " -"the build</quote>: one person can successfully commit part of a change and " -"then be blocked by the need for a merge, causing other people to see only a " -"portion of the work they intended to do. This also affects how you work with " -"project history. If you want to see all of the modifications someone made as " -"part of a task, you will need to manually inspect the descriptions and " -"timestamps of the changes made to each file involved (if you even know what " -"those files were)." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:579 -msgid "" -"CVS has a muddled notion of tags and branches that I will not attempt to even " -"describe. It does not support renaming of files or directories well, making " -"it easy to corrupt a repository. It has almost no internal consistency " -"checking capabilities, so it is usually not even possible to tell whether or " -"how a repository is corrupt. I would not recommend CVS for any project, " -"existing or new." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:587 -msgid "" -"Mercurial can import CVS revision history. However, there are a few caveats " -"that apply; these are true of every other revision control tool's CVS " -"importer, too. Due to CVS's lack of atomic changes and unversioned " -"filesystem hierarchy, it is not possible to reconstruct CVS history " -"completely accurately; some guesswork is involved, and renames will usually " -"not show up. Because a lot of advanced CVS administration has to be done by " -"hand and is hence error-prone, it's common for CVS importers to run into " -"multiple problems with corrupted repositories (completely bogus revision " -"timestamps and files that have remained locked for over a decade are just two " -"of the less interesting problems I can recall from personal experience)." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:601 -msgid "Mercurial can import revision history from a CVS repository." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch01-intro.xml:607 -msgid "Commercial tools" -msgstr "商业工具" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:609 -msgid "" -"Perforce has a centralised client/server architecture, with no client-side " -"caching of any data. Unlike modern revision control tools, Perforce requires " -"that a user run a command to inform the server about every file they intend " -"to edit." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:615 -msgid "" -"The performance of Perforce is quite good for small teams, but it falls off " -"rapidly as the number of users grows beyond a few dozen. Modestly large " -"Perforce installations require the deployment of proxies to cope with the " -"load their users generate." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch01-intro.xml:624 -msgid "Choosing a revision control tool" -msgstr "选择版本控制工具" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:626 -msgid "" -"With the exception of CVS, all of the tools listed above have unique " -"strengths that suit them to particular styles of work. There is no single " -"revision control tool that is best in all situations." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:631 -msgid "" -"As an example, Subversion is a good choice for working with frequently edited " -"binary files, due to its centralised nature and support for file locking." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch01-intro.xml:635 -msgid "" -"I personally find Mercurial's properties of simplicity, performance, and good " -"merge support to be a compelling combination that has served me well for " -"several years." -msgstr "" - -#. type: Content of: <book><chapter><sect1><title> -#: ../en/ch01-intro.xml:643 -msgid "Switching from another tool to Mercurial" -msgstr "从其它工具切换到 Mercurial" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:645 -msgid "" -"Mercurial is bundled with an extension named <literal role=\"hg-ext" -"\">convert</literal>, which can incrementally import revision history from " -"several other revision control tools. By <quote>incremental</quote>, I mean " -"that you can convert all of a project's history to date in one go, then rerun " -"the conversion later to obtain new changes that happened after the initial " -"conversion." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:653 -msgid "" -"The revision control tools supported by <literal role=\"hg-ext\">convert</" -"literal> are as follows:" -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch01-intro.xml:659 -msgid "Darcs" -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:661 -msgid "" -"In addition, <literal role=\"hg-ext\">convert</literal> can export changes " -"from Mercurial to Subversion. This makes it possible to try Subversion and " -"Mercurial in parallel before committing to a switchover, without risking the " -"loss of any work." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch01-intro.xml:667 -msgid "" -"The <command role=\"hg-ext-conver\">convert</command> command is easy to " -"use. Simply point it at the path or URL of the source repository, optionally " -"give it the name of the destination repository, and it will start working. " -"After the initial conversion, just run the same command again to import new " -"changes." -msgstr "" - -#. type: Content of: <book><chapter><title> -#: ../en/ch02-tour-basic.xml:5 +#: ../en/ch01-tour-basic.xml:5 msgid "A tour of Mercurial: the basics" msgstr "Mercurial 教程: 基础知识" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:8 +#: ../en/ch01-tour-basic.xml:8 msgid "Installing Mercurial on your system" msgstr "安装 Mercurial" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:10 +#: ../en/ch01-tour-basic.xml:10 msgid "" "Prebuilt binary packages of Mercurial are available for every popular " "operating system. These make it easy to start using Mercurial on your " @@ -2463,12 +1540,12 @@ "用 Mercurial 变得很容易。" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:15 +#: ../en/ch01-tour-basic.xml:15 msgid "Linux" msgstr "Linux" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:17 +#: ../en/ch01-tour-basic.xml:17 msgid "" "Because each Linux distribution has its own packaging tools, policies, and " "rate of development, it's difficult to give a comprehensive set of " @@ -2481,7 +1558,7 @@ "使用的发行版的 Mercurial 维护者的活跃程度。" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:24 +#: ../en/ch01-tour-basic.xml:24 msgid "" "To keep things simple, I will focus on installing Mercurial from the command " "line under the most popular Linux distributions. Most of these distributions " @@ -2493,39 +1570,39 @@ "Mercurial;寻找的包名称是 <literal>mercurial</literal>。" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:32 +#: ../en/ch01-tour-basic.xml:32 msgid "Debian:" msgstr "Debian:" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:34 +#: ../en/ch01-tour-basic.xml:34 msgid "Fedora Core:" msgstr "Fedora Core:" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:36 +#: ../en/ch01-tour-basic.xml:36 msgid "Gentoo:" msgstr "Gentoo:" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:38 +#: ../en/ch01-tour-basic.xml:38 msgid "OpenSUSE:" msgstr "OpenSUSE:" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:40 +#: ../en/ch01-tour-basic.xml:40 msgid "" "Ubuntu: Ubuntu's Mercurial package is based on Debian's. To install it, run " "the following command." msgstr "Ubuntu: Ubuntu 的 Mercurial 包基于 Debian。安装时,使用如下命令:" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:48 +#: ../en/ch01-tour-basic.xml:48 msgid "Solaris" msgstr "Solaris" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:50 +#: ../en/ch01-tour-basic.xml:50 msgid "" "SunFreeWare, at <ulink url=\"http://www.sunfreeware.com\">http://www." "sunfreeware.com</ulink>, is a good source for a large number of pre-built " @@ -2537,12 +1614,12 @@ "和 64 位包,包含 Mercurial 的当前版本。" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:58 +#: ../en/ch01-tour-basic.xml:58 msgid "Mac OS X" msgstr "Mac OS X" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:60 +#: ../en/ch01-tour-basic.xml:60 msgid "" "Lee Cantey publishes an installer of Mercurial for Mac OS X at <ulink url=" "\"http://mercurial.berkwood.com\">http://mercurial.berkwood.com</ulink>. " @@ -2553,7 +1630,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:69 +#: ../en/ch01-tour-basic.xml:69 msgid "" "It's also possible to install Mercurial using Fink or MacPorts, two popular " "free package managers for Mac OS X. If you have Fink, use <command>sudo apt-" @@ -2562,12 +1639,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:77 +#: ../en/ch01-tour-basic.xml:77 msgid "Windows" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:79 +#: ../en/ch01-tour-basic.xml:79 msgid "" "Lee Cantey publishes an installer of Mercurial for Windows at <ulink url=" "\"http://mercurial.berkwood.com\">http://mercurial.berkwood.com</ulink>. " @@ -2575,7 +1652,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch02-tour-basic.xml:86 +#: ../en/ch01-tour-basic.xml:86 msgid "" "The Windows version of Mercurial does not automatically convert line endings " "between Windows and Unix styles. If you want to share work with Unix users, " @@ -2583,12 +1660,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:96 +#: ../en/ch01-tour-basic.xml:96 msgid "Getting started" msgstr "开始" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:98 +#: ../en/ch01-tour-basic.xml:98 msgid "" "To begin, we'll use the <command role=\"hg-cmd\">hg version</command> command " "to find out whether Mercurial is actually installed properly. The actual " @@ -2597,12 +1674,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:107 +#: ../en/ch01-tour-basic.xml:107 msgid "Built-in help" msgstr "内置帮助" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:109 +#: ../en/ch01-tour-basic.xml:109 msgid "" "Mercurial provides a built-in help system. This is invaluable for those " "times when you find yourself stuck trying to remember how to run a command. " @@ -2613,7 +1690,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:120 +#: ../en/ch01-tour-basic.xml:120 msgid "" "For a more impressive level of detail (which you won't usually need) run " "<command role=\"hg-cmd\">hg help <option role=\"hg-opt-global\">-v</option></" @@ -2623,12 +1700,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:131 +#: ../en/ch01-tour-basic.xml:131 msgid "Working with a repository" msgstr "使用版本库" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:133 +#: ../en/ch01-tour-basic.xml:133 msgid "" "In Mercurial, everything happens inside a <emphasis>repository</emphasis>. " "The repository for a project contains all of the files that <quote>belong to</" @@ -2636,7 +1713,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:139 +#: ../en/ch01-tour-basic.xml:139 msgid "" "There's nothing particularly magical about a repository; it is simply a " "directory tree in your filesystem that Mercurial treats as special. You can " @@ -2645,12 +1722,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:146 +#: ../en/ch01-tour-basic.xml:146 msgid "Making a local copy of a repository" msgstr "创建版本库的工作副本" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:148 +#: ../en/ch01-tour-basic.xml:148 msgid "" "<emphasis>Copying</emphasis> a repository is just a little bit special. " "While you could use a normal file copying command to make a copy of a " @@ -2660,21 +1737,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:157 +#: ../en/ch01-tour-basic.xml:157 msgid "" "If our clone succeeded, we should now have a local directory called <filename " "class=\"directory\">hello</filename>. This directory will contain some files." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:163 +#: ../en/ch01-tour-basic.xml:163 msgid "" "These files have the same contents and history in our repository as they do " "in the repository we cloned." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:166 +#: ../en/ch01-tour-basic.xml:166 msgid "" "Every Mercurial repository is complete, self-contained, and independent. It " "contains its own private copy of a project's files and history. A cloned " @@ -2684,7 +1761,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:173 +#: ../en/ch01-tour-basic.xml:173 msgid "" "What this means for now is that we're free to experiment with our repository, " "safe in the knowledge that it's a private <quote>sandbox</quote> that won't " @@ -2692,12 +1769,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:179 +#: ../en/ch01-tour-basic.xml:179 msgid "What's in a repository?" msgstr "什么是版本库?" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:181 +#: ../en/ch01-tour-basic.xml:181 msgid "" "When we take a more detailed look inside a repository, we can see that it " "contains a directory named <filename class=\"directory\">.hg</filename>. " @@ -2705,7 +1782,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:188 +#: ../en/ch01-tour-basic.xml:188 msgid "" "The contents of the <filename class=\"directory\">.hg</filename> directory " "and its subdirectories are private to Mercurial. Every other file and " @@ -2713,7 +1790,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:194 +#: ../en/ch01-tour-basic.xml:194 msgid "" "To introduce a little terminology, the <filename class=\"directory\">.hg</" "filename> directory is the <quote>real</quote> repository, and all of the " @@ -2726,12 +1803,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:209 +#: ../en/ch01-tour-basic.xml:209 msgid "A tour through history" msgstr "回溯历史" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:211 +#: ../en/ch01-tour-basic.xml:211 msgid "" "One of the first things we might want to do with a new, unfamiliar repository " "is understand its history. The <command role=\"hg-cmd\">hg log</command> " @@ -2739,7 +1816,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:218 +#: ../en/ch01-tour-basic.xml:218 msgid "" "By default, this command prints a brief paragraph of output for each change " "to the project that was recorded. In Mercurial terminology, we call each of " @@ -2748,14 +1825,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:224 +#: ../en/ch01-tour-basic.xml:224 msgid "" "The fields in a record of output from <command role=\"hg-cmd\">hg log</" "command> are as follows." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:227 +#: ../en/ch01-tour-basic.xml:227 msgid "" "<literal>changeset</literal>: This field has the format of a number, followed " "by a colon, followed by a hexadecimal string. These are " @@ -2765,7 +1842,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:233 +#: ../en/ch01-tour-basic.xml:233 msgid "" "<literal>user</literal>: The identity of the person who created the " "changeset. This is a free-form field, but it most often contains a person's " @@ -2773,7 +1850,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:237 +#: ../en/ch01-tour-basic.xml:237 msgid "" "<literal>date</literal>: The date and time on which the changeset was " "created, and the timezone in which it was created. (The date and time are " @@ -2782,21 +1859,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:242 +#: ../en/ch01-tour-basic.xml:242 msgid "" "<literal>summary</literal>: The first line of the text message that the " "creator of the changeset entered to describe the changeset." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:245 +#: ../en/ch01-tour-basic.xml:245 msgid "" "The default output printed by <command role=\"hg-cmd\">hg log</command> is " "purely a summary; it is missing a lot of detail." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:249 +#: ../en/ch01-tour-basic.xml:249 msgid "" "Figure <xref endterm=\"fig.tour-basic.history.caption\" linkend=\"fig.tour-" "basic.history\"/> provides a graphical representation of the history of the " @@ -2807,39 +1884,39 @@ msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject> -#: ../en/ch02-tour-basic.xml:260 +#: ../en/ch01-tour-basic.xml:260 msgid "" "<imageobject><imagedata fileref=\"images/tour-history.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><textobject><phrase> -#: ../en/ch02-tour-basic.xml:261 ../en/ch03-tour-merge.xml:48 -#: ../en/ch03-tour-merge.xml:78 ../en/ch03-tour-merge.xml:126 -#: ../en/ch03-tour-merge.xml:182 ../en/ch03-tour-merge.xml:254 -#: ../en/ch04-concepts.xml:56 ../en/ch04-concepts.xml:108 -#: ../en/ch04-concepts.xml:194 ../en/ch04-concepts.xml:301 -#: ../en/ch04-concepts.xml:353 ../en/ch04-concepts.xml:370 -#: ../en/ch04-concepts.xml:414 ../en/ch04-concepts.xml:436 -#: ../en/ch04-concepts.xml:480 ../en/ch06-collab.xml:277 -#: ../en/ch09-undo.xml:366 ../en/ch09-undo.xml:417 ../en/ch09-undo.xml:485 -#: ../en/ch09-undo.xml:527 ../en/ch12-mq.xml:409 +#: ../en/ch01-tour-basic.xml:261 ../en/ch02-tour-merge.xml:48 +#: ../en/ch02-tour-merge.xml:78 ../en/ch02-tour-merge.xml:126 +#: ../en/ch02-tour-merge.xml:182 ../en/ch02-tour-merge.xml:254 +#: ../en/ch03-concepts.xml:56 ../en/ch03-concepts.xml:108 +#: ../en/ch03-concepts.xml:194 ../en/ch03-concepts.xml:301 +#: ../en/ch03-concepts.xml:353 ../en/ch03-concepts.xml:370 +#: ../en/ch03-concepts.xml:414 ../en/ch03-concepts.xml:436 +#: ../en/ch03-concepts.xml:480 ../en/ch05-collab.xml:277 +#: ../en/ch08-undo.xml:366 ../en/ch08-undo.xml:417 ../en/ch08-undo.xml:485 +#: ../en/ch08-undo.xml:527 ../en/ch11-mq.xml:410 msgid "XXX add text" msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject><caption><para> -#: ../en/ch02-tour-basic.xml:262 +#: ../en/ch01-tour-basic.xml:262 msgid "" "Graphical history of the <filename class=\"directory\">hello</filename> " "repository" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:269 +#: ../en/ch01-tour-basic.xml:269 msgid "Changesets, revisions, and talking to other people" msgstr "改变集,版本,与其它用户交互" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:272 +#: ../en/ch01-tour-basic.xml:272 msgid "" "As English is a notoriously sloppy language, and computer science has a " "hallowed history of terminological confusion (why use one term when four will " @@ -2851,7 +1928,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:282 +#: ../en/ch01-tour-basic.xml:282 msgid "" "While it doesn't matter what <emphasis>word</emphasis> you use to refer to " "the concept of <quote>a changeset</quote>, the <emphasis>identifier</" @@ -2862,13 +1939,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:291 +#: ../en/ch01-tour-basic.xml:291 msgid "" "The revision number is <emphasis>only valid in that repository</emphasis>," msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:293 +#: ../en/ch01-tour-basic.xml:293 msgid "" "while the hex string is the <emphasis>permanent, unchanging identifier</" "emphasis> that will always identify that exact changeset in <emphasis>every</" @@ -2876,7 +1953,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:298 +#: ../en/ch01-tour-basic.xml:298 msgid "" "This distinction is important. If you send someone an email talking about " "<quote>revision 33</quote>, there's a high likelihood that their revision 33 " @@ -2888,7 +1965,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:308 +#: ../en/ch01-tour-basic.xml:308 msgid "" "Mercurial uses revision numbers purely as a convenient shorthand. If you " "need to discuss a changeset with someone, or make a record of a changeset for " @@ -2897,12 +1974,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:316 +#: ../en/ch01-tour-basic.xml:316 msgid "Viewing specific revisions" msgstr "察看指定版本" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:318 +#: ../en/ch01-tour-basic.xml:318 msgid "" "To narrow the output of <command role=\"hg-cmd\">hg log</command> down to a " "single revision, use the <option role=\"hg-opt-log\">-r</option> (or <option " @@ -2912,7 +1989,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:327 +#: ../en/ch01-tour-basic.xml:327 msgid "" "If you want to see the history of several revisions without having to list " "each one, you can use <emphasis>range notation</emphasis>; this lets you " @@ -2921,7 +1998,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:335 +#: ../en/ch01-tour-basic.xml:335 msgid "" "Mercurial also honours the order in which you specify revisions, so <command " "role=\"hg-cmd\">hg log -r 2:4</command> prints 2, 3, and 4. while <command " @@ -2929,12 +2006,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:342 +#: ../en/ch01-tour-basic.xml:342 msgid "More detailed information" msgstr "更详细的信息" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:344 +#: ../en/ch01-tour-basic.xml:344 msgid "" "While the summary information printed by <command role=\"hg-cmd\">hg log</" "command> is useful if you already know what you're looking for, you may need " @@ -2946,7 +2023,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:356 +#: ../en/ch01-tour-basic.xml:356 msgid "" "If you want to see both the description and content of a change, add the " "<option role=\"hg-opt-log\">-p</option> (or <option role=\"hg-opt-log\">--" @@ -2956,12 +2033,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:369 +#: ../en/ch01-tour-basic.xml:369 msgid "All about command options" msgstr "命令选项" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:371 +#: ../en/ch01-tour-basic.xml:371 msgid "" "Let's take a brief break from exploring Mercurial commands to discuss a " "pattern in the way that they work; you may find this useful to keep in mind " @@ -2969,7 +2046,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:375 +#: ../en/ch01-tour-basic.xml:375 msgid "" "Mercurial has a consistent and straightforward approach to dealing with the " "options that you can pass to commands. It follows the conventions for " @@ -2977,7 +2054,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:380 +#: ../en/ch01-tour-basic.xml:380 msgid "" "Every option has a long name. For example, as we've already seen, the " "<command role=\"hg-cmd\">hg log</command> command accepts a <option role=\"hg-" @@ -2985,7 +2062,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:384 +#: ../en/ch01-tour-basic.xml:384 msgid "" "Most options have short names, too. Instead of <option role=\"hg-opt-log\">--" "rev</option>, we can use <option role=\"hg-opt-log\">-r</option>. (The " @@ -2994,7 +2071,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:389 +#: ../en/ch01-tour-basic.xml:389 msgid "" "Long options start with two dashes (e.g. <option role=\"hg-opt-log\">--rev</" "option>), while short options start with one (e.g. <option role=\"hg-opt-log" @@ -3002,7 +2079,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:393 +#: ../en/ch01-tour-basic.xml:393 msgid "" "Option naming and usage is consistent across commands. For example, every " "command that lets you specify a changeset ID or revision number accepts both " @@ -3011,7 +2088,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:399 +#: ../en/ch01-tour-basic.xml:399 msgid "" "In the examples throughout this book, I use short options instead of long. " "This just reflects my own preference, so don't read anything significant into " @@ -3019,7 +2096,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:403 +#: ../en/ch01-tour-basic.xml:403 msgid "" "Most commands that print output of some kind will print more output when " "passed a <option role=\"hg-opt-global\">-v</option> (or <option role=\"hg-opt-" @@ -3028,19 +2105,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:411 +#: ../en/ch01-tour-basic.xml:411 msgid "Making and reviewing changes" msgstr "创建和复审修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:413 +#: ../en/ch01-tour-basic.xml:413 msgid "" "Now that we have a grasp of viewing history in Mercurial, let's take a look " "at making some changes and examining them." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:417 +#: ../en/ch01-tour-basic.xml:417 msgid "" "The first thing we'll do is isolate our experiment in a repository of its " "own. We use the <command role=\"hg-cmd\">hg clone</command> command, but we " @@ -3051,7 +2128,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:427 +#: ../en/ch01-tour-basic.xml:427 msgid "" "As an aside, it's often good practice to keep a <quote>pristine</quote> copy " "of a remote repository around, which you can then make temporary clones of to " @@ -3063,7 +2140,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:436 +#: ../en/ch01-tour-basic.xml:436 msgid "" "In our <filename class=\"directory\">my-hello</filename> repository, we have " "a file <filename>hello.c</filename> that contains the classic <quote>hello, " @@ -3076,14 +2153,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:449 +#: ../en/ch01-tour-basic.xml:449 msgid "" "Mercurial's <command role=\"hg-cmd\">hg status</command> command will tell us " "what Mercurial knows about the files in the repository." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:455 +#: ../en/ch01-tour-basic.xml:455 msgid "" "The <command role=\"hg-cmd\">hg status</command> command prints no output for " "some files, but a line starting with <quote><literal>M</literal></quote> for " @@ -3093,7 +2170,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:462 +#: ../en/ch01-tour-basic.xml:462 msgid "" "The <quote><literal>M</literal></quote> indicates that Mercurial has noticed " "that we modified <filename>hello.c</filename>. We didn't need to " @@ -3103,7 +2180,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:470 +#: ../en/ch01-tour-basic.xml:470 msgid "" "It's a little bit helpful to know that we've modified <filename>hello.c</" "filename>, but we might prefer to know exactly <emphasis>what</emphasis> " @@ -3112,12 +2189,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:480 +#: ../en/ch01-tour-basic.xml:480 msgid "Recording changes in a new changeset" msgstr "在新修改集中记录修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:482 +#: ../en/ch01-tour-basic.xml:482 msgid "" "We can modify files, build and test our changes, and use <command role=\"hg-" "cmd\">hg status</command> and <command role=\"hg-cmd\">hg diff</command> to " @@ -3126,7 +2203,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:489 +#: ../en/ch01-tour-basic.xml:489 msgid "" "The <command role=\"hg-cmd\">hg commit</command> command lets us create a new " "changeset; we'll usually refer to this as <quote>making a commit</quote> or " @@ -3134,12 +2211,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:495 +#: ../en/ch01-tour-basic.xml:495 msgid "Setting up a username" msgstr "配置用户名称" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:497 +#: ../en/ch01-tour-basic.xml:497 msgid "" "When you try to run <command role=\"hg-cmd\">hg commit</command> for the " "first time, it is not guaranteed to succeed. Mercurial records your name and " @@ -3150,7 +2227,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:506 +#: ../en/ch01-tour-basic.xml:506 msgid "" "If you specify a <option role=\"hg-opt-commit\">-u</option> option to the " "<command role=\"hg-cmd\">hg commit</command> command on the command line, " @@ -3158,14 +2235,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:511 +#: ../en/ch01-tour-basic.xml:511 msgid "" "If you have set the <envar>HGUSER</envar> environment variable, this is " "checked next." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:514 +#: ../en/ch01-tour-basic.xml:514 msgid "" "If you create a file in your home directory called <filename role=\"special" "\">.hgrc</filename>, with a <envar role=\"rc-item-ui\">username</envar> " @@ -3174,14 +2251,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:521 +#: ../en/ch01-tour-basic.xml:521 msgid "" "If you have set the <envar>EMAIL</envar> environment variable, this will be " "used next." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch02-tour-basic.xml:524 +#: ../en/ch01-tour-basic.xml:524 msgid "" "Mercurial will query your system to find out your local user name and host " "name, and construct a username from these components. Since this often " @@ -3190,7 +2267,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:531 +#: ../en/ch01-tour-basic.xml:531 msgid "" "If all of these mechanisms fail, Mercurial will fail, printing an error " "message. In this case, it will not let you commit until you set up a " @@ -3198,7 +2275,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:535 +#: ../en/ch01-tour-basic.xml:535 msgid "" "You should think of the <envar>HGUSER</envar> environment variable and the " "<option role=\"hg-opt-commit\">-u</option> option to the <command role=\"hg-" @@ -3209,12 +2286,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch02-tour-basic.xml:544 +#: ../en/ch01-tour-basic.xml:544 msgid "Creating a Mercurial configuration file" msgstr "创建 Mercurial 的配置文件" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch02-tour-basic.xml:546 +#: ../en/ch01-tour-basic.xml:546 msgid "" "To set a user name, use your favourite editor to create a file called " "<filename role=\"special\">.hgrc</filename> in your home directory. " @@ -3224,7 +2301,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch02-tour-basic.xml:557 +#: ../en/ch01-tour-basic.xml:558 msgid "" "The <quote><literal>[ui]</literal></quote> line begins a <emphasis>section</" "emphasis> of the config file, so you can read the <quote><literal>username " @@ -3236,12 +2313,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch02-tour-basic.xml:570 +#: ../en/ch01-tour-basic.xml:571 msgid "Choosing a user name" msgstr "选择用户名称" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch02-tour-basic.xml:572 +#: ../en/ch01-tour-basic.xml:573 msgid "" "You can use any text you like as the value of the <literal>username</literal> " "config item, since this information is for reading by other people, but for " @@ -3250,7 +2327,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><note><para> -#: ../en/ch02-tour-basic.xml:579 +#: ../en/ch01-tour-basic.xml:580 msgid "" "Mercurial's built-in web server obfuscates email addresses, to make it more " "difficult for the email harvesting tools that spammers use. This reduces the " @@ -3259,12 +2336,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:589 +#: ../en/ch01-tour-basic.xml:590 msgid "Writing a commit message" msgstr "写提交日志" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:591 +#: ../en/ch01-tour-basic.xml:592 msgid "" "When we commit a change, Mercurial drops us into a text editor, to enter a " "message that will describe the modifications we've made in this changeset. " @@ -3274,7 +2351,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:601 +#: ../en/ch01-tour-basic.xml:602 msgid "" "The editor that the <command role=\"hg-cmd\">hg commit</command> command " "drops us into will contain an empty line, followed by a number of lines " @@ -3282,7 +2359,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:608 +#: ../en/ch01-tour-basic.xml:609 msgid "" "Mercurial ignores the lines that start with <quote><literal>HG:</literal></" "quote>; it uses them only to tell us which files it's recording changes to. " @@ -3290,12 +2367,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:614 +#: ../en/ch01-tour-basic.xml:615 msgid "Writing a good commit message" msgstr "写好提交日志" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:616 +#: ../en/ch01-tour-basic.xml:617 msgid "" "Since <command role=\"hg-cmd\">hg log</command> only prints the first line of " "a commit message by default, it's best to write a commit message whose first " @@ -3305,7 +2382,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:630 +#: ../en/ch01-tour-basic.xml:631 msgid "" "As far as the remainder of the contents of the commit message are concerned, " "there are no hard-and-fast rules. Mercurial itself doesn't interpret or care " @@ -3314,7 +2391,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:636 +#: ../en/ch01-tour-basic.xml:637 msgid "" "My personal preference is for short, but informative, commit messages that " "tell me something that I can't figure out with a quick glance at the output " @@ -3322,12 +2399,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:643 +#: ../en/ch01-tour-basic.xml:644 msgid "Aborting a commit" msgstr "终止提交" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:645 +#: ../en/ch01-tour-basic.xml:646 msgid "" "If you decide that you don't want to commit while in the middle of editing a " "commit message, simply exit from your editor without saving the file that " @@ -3336,7 +2413,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:650 +#: ../en/ch01-tour-basic.xml:651 msgid "" "If we run the <command role=\"hg-cmd\">hg commit</command> command without " "any arguments, it records all of the changes we've made, as reported by " @@ -3345,12 +2422,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:657 +#: ../en/ch01-tour-basic.xml:658 msgid "Admiring our new handiwork" msgstr "欣赏我们的新手艺" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:659 +#: ../en/ch01-tour-basic.xml:660 msgid "" "Once we've finished the commit, we can use the <command role=\"hg-cmd\">hg " "tip</command> command to display the changeset we just created. This command " @@ -3359,19 +2436,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:668 +#: ../en/ch01-tour-basic.xml:669 msgid "" "We refer to the newest revision in the repository as the tip revision, or " "simply the tip." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch02-tour-basic.xml:675 +#: ../en/ch01-tour-basic.xml:676 msgid "Sharing changes" msgstr "共享修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch02-tour-basic.xml:677 +#: ../en/ch01-tour-basic.xml:678 msgid "" "We mentioned earlier that repositories in Mercurial are self-contained. This " "means that the changeset we just created exists only in our <filename class=" @@ -3380,12 +2457,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:685 +#: ../en/ch01-tour-basic.xml:686 msgid "Pulling changes from another repository" msgstr "从其它版本库取得修改" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:686 +#: ../en/ch01-tour-basic.xml:687 msgid "" "To get started, let's clone our original <filename class=\"directory\">hello</" "filename> repository, which does not contain the change we just committed. " @@ -3394,7 +2471,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:694 +#: ../en/ch01-tour-basic.xml:695 msgid "" "We'll use the <command role=\"hg-cmd\">hg pull</command> command to bring " "changes from <filename class=\"directory\">my-hello</filename> into <filename " @@ -3407,7 +2484,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:707 +#: ../en/ch01-tour-basic.xml:708 msgid "" "(Of course, someone could cause more changesets to appear in the repository " "that we ran <command role=\"hg-cmd\">hg incoming</command> in, before we get " @@ -3416,7 +2493,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:714 +#: ../en/ch01-tour-basic.xml:715 msgid "" "Bringing changes into a repository is a simple matter of running the <command " "role=\"hg-cmd\">hg pull</command> command, and telling it which repository to " @@ -3424,7 +2501,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:721 +#: ../en/ch01-tour-basic.xml:722 msgid "" "As you can see from the before-and-after output of <command role=\"hg-cmd" "\">hg tip</command>, we have successfully pulled changes into our " @@ -3433,12 +2510,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:729 +#: ../en/ch01-tour-basic.xml:730 msgid "Updating the working directory" msgstr "更新工作目录" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:731 +#: ../en/ch01-tour-basic.xml:732 msgid "" "We have so far glossed over the relationship between a repository and its " "working directory. The <command role=\"hg-cmd\">hg pull</command> command " @@ -3450,7 +2527,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:743 +#: ../en/ch01-tour-basic.xml:744 msgid "" "It might seem a bit strange that <command role=\"hg-cmd\">hg pull</command> " "doesn't update the working directory automatically. There's actually a good " @@ -3464,7 +2541,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:754 +#: ../en/ch01-tour-basic.xml:755 msgid "" "However, since pull-then-update is such a common thing to do, Mercurial lets " "you combine the two by passing the <option role=\"hg-opt-pull\">-u</option> " @@ -3472,7 +2549,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:759 +#: ../en/ch01-tour-basic.xml:760 msgid "" "If you look back at the output of <command role=\"hg-cmd\">hg pull</command> " "in section <xref linkend=\"sec.tour.pull\"/> when we ran it without <option " @@ -3482,14 +2559,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:768 +#: ../en/ch01-tour-basic.xml:769 msgid "" "To find out what revision the working directory is at, use the <command role=" "\"hg-cmd\">hg parents</command> command." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:774 +#: ../en/ch01-tour-basic.xml:775 msgid "" "If you look back at figure <xref endterm=\"fig.tour-basic.history.caption\" " "linkend=\"fig.tour-basic.history\"/>, you'll see arrows connecting each " @@ -3500,7 +2577,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:784 +#: ../en/ch01-tour-basic.xml:785 msgid "" "To update the working directory to a particular revision, give a revision " "number or changeset ID to the <command role=\"hg-cmd\">hg update</command> " @@ -3508,7 +2585,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:791 +#: ../en/ch01-tour-basic.xml:792 msgid "" "If you omit an explicit revision, <command role=\"hg-cmd\">hg update</" "command> will update to the tip revision, as shown by the second call to " @@ -3516,12 +2593,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:799 +#: ../en/ch01-tour-basic.xml:800 msgid "Pushing changes to another repository" msgstr "发布修改到其它版本库" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:801 +#: ../en/ch01-tour-basic.xml:802 msgid "" "Mercurial lets us push changes to another repository, from the repository " "we're currently visiting. As with the example of <command role=\"hg-cmd\">hg " @@ -3530,21 +2607,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:809 +#: ../en/ch01-tour-basic.xml:810 msgid "" "The <command role=\"hg-cmd\">hg outgoing</command> command tells us what " "changes would be pushed into another repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:815 +#: ../en/ch01-tour-basic.xml:816 msgid "" "And the <command role=\"hg-cmd\">hg push</command> command does the actual " "push." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:821 +#: ../en/ch01-tour-basic.xml:822 msgid "" "As with <command role=\"hg-cmd\">hg pull</command>, the <command role=\"hg-cmd" "\">hg push</command> command does not update the working directory in the " @@ -3555,19 +2632,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:830 +#: ../en/ch01-tour-basic.xml:831 msgid "" "What happens if we try to pull or push changes and the receiving repository " "already has those changes? Nothing too exciting." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch02-tour-basic.xml:837 +#: ../en/ch01-tour-basic.xml:838 msgid "Sharing changes over a network" msgstr "通过网络共享修改" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:839 +#: ../en/ch01-tour-basic.xml:840 msgid "" "The commands we have covered in the previous few sections are not limited to " "working with local repositories. Each works in exactly the same fashion over " @@ -3575,7 +2652,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch02-tour-basic.xml:847 +#: ../en/ch01-tour-basic.xml:848 msgid "" "In this example, we can see what changes we could push to the remote " "repository, but the repository is understandably not set up to let anonymous " @@ -3583,12 +2660,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch03-tour-merge.xml:5 +#: ../en/ch02-tour-merge.xml:5 msgid "A tour of Mercurial: merging work" msgstr "Mercurial 教程: 合并工作" #. type: Content of: <book><chapter><para> -#: ../en/ch03-tour-merge.xml:7 +#: ../en/ch02-tour-merge.xml:7 msgid "" "We've now covered cloning a repository, making changes in a repository, and " "pulling or pushing changes from one repository into another. Our next step " @@ -3596,19 +2673,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch03-tour-merge.xml:13 +#: ../en/ch02-tour-merge.xml:13 msgid "Merging streams of work" msgstr "合并的流程" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:15 +#: ../en/ch02-tour-merge.xml:15 msgid "" "Merging is a fundamental part of working with a distributed revision control " "tool." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch03-tour-merge.xml:18 +#: ../en/ch02-tour-merge.xml:18 msgid "" "Alice and Bob each have a personal copy of a repository for a project they're " "collaborating on. Alice fixes a bug in her repository; Bob adds a new " @@ -3617,7 +2694,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch03-tour-merge.xml:24 +#: ../en/ch02-tour-merge.xml:24 msgid "" "I frequently work on several different tasks for a single project at once, " "each safely isolated in its own repository. Working this way means that I " @@ -3625,7 +2702,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:30 +#: ../en/ch02-tour-merge.xml:30 msgid "" "Because merging is such a common thing to need to do, Mercurial makes it " "easy. Let's walk through the process. We'll begin by cloning yet another " @@ -3633,7 +2710,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:37 +#: ../en/ch02-tour-merge.xml:37 msgid "" "We should now have two copies of <filename>hello.c</filename> with different " "contents. The histories of the two repositories have also diverged, as " @@ -3642,14 +2719,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject> -#: ../en/ch03-tour-merge.xml:47 +#: ../en/ch02-tour-merge.xml:47 msgid "" "<imageobject><imagedata fileref=\"images/tour-merge-sep-repos.png\"/></" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject><caption><para> -#: ../en/ch03-tour-merge.xml:49 +#: ../en/ch02-tour-merge.xml:49 msgid "" "Divergent recent histories of the <filename class=\"directory\">my-hello</" "filename> and <filename class=\"directory\">my-new-hello</filename> " @@ -3657,7 +2734,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:57 +#: ../en/ch02-tour-merge.xml:57 msgid "" "We already know that pulling changes from our <filename class=\"directory" "\">my-hello</filename> repository will have no effect on the working " @@ -3665,19 +2742,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:63 +#: ../en/ch02-tour-merge.xml:63 msgid "" "However, the <command role=\"hg-cmd\">hg pull</command> command says " "something about <quote>heads</quote>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch03-tour-merge.xml:67 +#: ../en/ch02-tour-merge.xml:67 msgid "Head changesets" msgstr "顶点改变集" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:69 +#: ../en/ch02-tour-merge.xml:69 msgid "" "A head is a change that has no descendants, or children, as they're also " "known. The tip revision is thus a head, because the newest revision in a " @@ -3686,20 +2763,20 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch03-tour-merge.xml:77 +#: ../en/ch02-tour-merge.xml:77 msgid "" "<imageobject><imagedata fileref=\"images/tour-merge-pull.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch03-tour-merge.xml:79 +#: ../en/ch02-tour-merge.xml:79 msgid "" "Repository contents after pulling from <filename class=\"directory\">my-" "hello</filename> into <filename class=\"directory\">my-new-hello</filename>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:85 +#: ../en/ch02-tour-merge.xml:85 msgid "" "In figure <xref endterm=\"fig.tour-merge.pull.caption\" linkend=\"fig.tour-" "merge.pull\"/>, you can see the effect of the pull from <filename class=" @@ -3716,19 +2793,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch03-tour-merge.xml:106 +#: ../en/ch02-tour-merge.xml:106 msgid "Performing the merge" msgstr "执行合并" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:108 +#: ../en/ch02-tour-merge.xml:108 msgid "" "What happens if we try to use the normal <command role=\"hg-cmd\">hg update</" "command> command to update to the new tip?" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:114 +#: ../en/ch02-tour-merge.xml:114 msgid "" "Mercurial is telling us that the <command role=\"hg-cmd\">hg update</command> " "command won't do a merge; it won't update the working directory when it " @@ -3738,19 +2815,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch03-tour-merge.xml:125 +#: ../en/ch02-tour-merge.xml:125 msgid "" "<imageobject><imagedata fileref=\"images/tour-merge-merge.png\"/></" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch03-tour-merge.xml:127 +#: ../en/ch02-tour-merge.xml:127 msgid "Working directory and repository during merge, and following commit" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:132 +#: ../en/ch02-tour-merge.xml:132 msgid "" "This updates the working directory so that it contains changes from " "<emphasis>both</emphasis> heads, which is reflected in both the output of " @@ -3759,12 +2836,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch03-tour-merge.xml:142 +#: ../en/ch02-tour-merge.xml:142 msgid "Committing the results of the merge" msgstr "提交合并结果" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:144 +#: ../en/ch02-tour-merge.xml:144 msgid "" "Whenever we've done a merge, <command role=\"hg-cmd\">hg parents</command> " "will display two parents until we <command role=\"hg-cmd\">hg commit</" @@ -3772,7 +2849,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:151 +#: ../en/ch02-tour-merge.xml:151 msgid "" "We now have a new tip revision; notice that it has <emphasis>both</emphasis> " "of our former heads as its parents. These are the same revisions that were " @@ -3780,7 +2857,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:158 +#: ../en/ch02-tour-merge.xml:158 msgid "" "In figure <xref endterm=\"fig.tour-merge.merge.caption\" linkend=\"fig.tour-" "merge.merge\"/>, you can see a representation of what happens to the working " @@ -3790,12 +2867,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch03-tour-merge.xml:169 +#: ../en/ch02-tour-merge.xml:169 msgid "Merging conflicting changes" msgstr "合并有冲突的改变" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:171 +#: ../en/ch02-tour-merge.xml:171 msgid "" "Most merges are simple affairs, but sometimes you'll find yourself merging " "changes where each modifies the same portions of the same files. Unless both " @@ -3805,19 +2882,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject> -#: ../en/ch03-tour-merge.xml:180 +#: ../en/ch02-tour-merge.xml:180 msgid "" "<imageobject><imagedata fileref=\"images/tour-merge-conflict.png\"/> </" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject><caption><para> -#: ../en/ch03-tour-merge.xml:183 +#: ../en/ch02-tour-merge.xml:183 msgid "Conflicting changes to a document" msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:188 +#: ../en/ch02-tour-merge.xml:188 msgid "" "Figure <xref endterm=\"fig.tour-merge.conflict.caption\" linkend=\"fig.tour-" "merge.conflict\"/> illustrates an instance of two conflicting changes to a " @@ -3828,7 +2905,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:196 +#: ../en/ch02-tour-merge.xml:196 msgid "" "Mercurial doesn't have a built-in facility for handling conflicts. Instead, " "it runs an external program called <command>hgmerge</command>. This is a " @@ -3841,7 +2918,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:207 +#: ../en/ch02-tour-merge.xml:207 msgid "" "It's also possible to get Mercurial to run another program or script instead " "of <command>hgmerge</command>, by setting the <envar>HGMERGE</envar> " @@ -3849,12 +2926,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch03-tour-merge.xml:213 +#: ../en/ch02-tour-merge.xml:213 msgid "Using a graphical merge tool" msgstr "使用图形合并工具" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:215 +#: ../en/ch02-tour-merge.xml:215 msgid "" "My preferred graphical merge tool is <command>kdiff3</command>, which I'll " "use to describe the features that are common to graphical file merging " @@ -3867,7 +2944,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch03-tour-merge.xml:226 +#: ../en/ch02-tour-merge.xml:226 msgid "" "At the left is the <emphasis>base</emphasis> version of the file, i.e. the " "most recent version from which the two versions we're trying to merge are " @@ -3875,21 +2952,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch03-tour-merge.xml:231 +#: ../en/ch02-tour-merge.xml:231 msgid "" "In the middle is <quote>our</quote> version of the file, with the contents " "that we modified." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch03-tour-merge.xml:234 +#: ../en/ch02-tour-merge.xml:234 msgid "" "On the right is <quote>their</quote> version of the file, the one that from " "the changeset that we're trying to merge with." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:238 +#: ../en/ch02-tour-merge.xml:238 msgid "" "In the pane below these is the current <emphasis>result</emphasis> of the " "merge. Our task is to replace all of the red text, which indicates unresolved " @@ -3898,7 +2975,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:245 +#: ../en/ch02-tour-merge.xml:245 msgid "" "All four of these panes are <emphasis>locked together</emphasis>; if we " "scroll vertically or horizontally in any of them, the others are updated to " @@ -3906,19 +2983,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch03-tour-merge.xml:252 +#: ../en/ch02-tour-merge.xml:252 msgid "" "<imageobject><imagedata width=\"100%\" fileref=\"images/kdiff3.png\"/> </" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch03-tour-merge.xml:255 +#: ../en/ch02-tour-merge.xml:255 msgid "Using <command>kdiff3</command> to merge versions of a file" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:261 +#: ../en/ch02-tour-merge.xml:261 msgid "" "For each conflicting portion of the file, we can choose to resolve the " "conflict using some combination of text from the base version, ours, or " @@ -3927,7 +3004,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:267 +#: ../en/ch02-tour-merge.xml:267 msgid "" "There are <emphasis>many</emphasis> file merging tools available, too many to " "cover here. They vary in which platforms they are available for, and in " @@ -3937,12 +3014,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch03-tour-merge.xml:276 +#: ../en/ch02-tour-merge.xml:276 msgid "A worked example" msgstr "合并实例" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:278 +#: ../en/ch02-tour-merge.xml:278 msgid "" "In this example, we will reproduce the file modification history of figure " "<xref endterm=\"fig.tour-merge.conflict.caption\" linkend=\"fig.tour-merge." @@ -3951,12 +3028,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:286 +#: ../en/ch02-tour-merge.xml:286 msgid "We'll clone the repository and make a change to the file." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:291 +#: ../en/ch02-tour-merge.xml:291 msgid "" "And another clone, to simulate someone else making a change to the file. " "(This hints at the idea that it's not all that unusual to merge with yourself " @@ -3965,14 +3042,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:299 +#: ../en/ch02-tour-merge.xml:299 msgid "" "Having created two different versions of the file, we'll set up an " "environment suitable for running our merge." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:305 +#: ../en/ch02-tour-merge.xml:305 msgid "" "In this example, I won't use Mercurial's normal <command>hgmerge</command> " "program to do the merge, because it would drop my nice automated example-" @@ -3984,12 +3061,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:315 +#: ../en/ch02-tour-merge.xml:315 msgid "<emphasis role=\"bold\">XXX FIX THIS EXAMPLE.</emphasis>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:320 +#: ../en/ch02-tour-merge.xml:320 msgid "" "Because <command>merge</command> can't resolve the conflicting changes, it " "leaves <emphasis>merge markers</emphasis> inside the file that has conflicts, " @@ -3998,7 +3075,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:326 +#: ../en/ch02-tour-merge.xml:326 msgid "" "Mercurial can tell from the way <command>merge</command> exits that it wasn't " "able to merge successfully, so it tells us what commands we'll need to run if " @@ -4008,7 +3085,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch03-tour-merge.xml:333 +#: ../en/ch02-tour-merge.xml:333 msgid "" "If automatic or manual merges fail, there's nothing to prevent us from " "<quote>fixing up</quote> the affected files ourselves, and committing the " @@ -4016,19 +3093,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch03-tour-merge.xml:342 +#: ../en/ch02-tour-merge.xml:342 msgid "Simplifying the pull-merge-commit sequence" msgstr "简化拉-合并-提交程序" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:344 +#: ../en/ch02-tour-merge.xml:344 msgid "" "The process of merging changes as outlined above is straightforward, but " "requires running three commands in sequence." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:350 +#: ../en/ch02-tour-merge.xml:350 msgid "" "In the case of the final commit, you also need to enter a commit message, " "which is almost always going to be a piece of uninteresting " @@ -4036,7 +3113,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:354 +#: ../en/ch02-tour-merge.xml:354 msgid "" "It would be nice to reduce the number of steps needed, if this were " "possible. Indeed, Mercurial is distributed with an extension called <literal " @@ -4044,7 +3121,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:359 +#: ../en/ch02-tour-merge.xml:359 msgid "" "Mercurial provides a flexible extension mechanism that lets people extend its " "functionality, while keeping the core of Mercurial small and easy to deal " @@ -4054,7 +3131,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:366 +#: ../en/ch02-tour-merge.xml:366 msgid "" "The <literal role=\"hg-ext\">fetch</literal> extension adds a new command " "called, not surprisingly, <command role=\"hg-cmd\">hg fetch</command>. This " @@ -4068,7 +3145,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:379 +#: ../en/ch02-tour-merge.xml:379 msgid "" "Enabling the <literal role=\"hg-ext\">fetch</literal> extension is easy. " "Edit your <filename role=\"special\">.hgrc</filename>, and either go to the " @@ -4078,7 +3155,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch03-tour-merge.xml:389 +#: ../en/ch02-tour-merge.xml:388 msgid "" "(Normally, on the right-hand side of the <quote><literal>=</literal></quote> " "would appear the location of the extension, but since the <literal role=\"hg-" @@ -4087,12 +3164,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch04-concepts.xml:5 +#: ../en/ch03-concepts.xml:5 msgid "Behind the scenes" msgstr "Mercurial 内幕" #. type: Content of: <book><chapter><para> -#: ../en/ch04-concepts.xml:7 +#: ../en/ch03-concepts.xml:7 msgid "" "Unlike many revision control systems, the concepts upon which Mercurial is " "built are simple enough that it's easy to understand how the software really " @@ -4101,7 +3178,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch04-concepts.xml:13 +#: ../en/ch03-concepts.xml:13 msgid "" "This understanding gives me confidence that Mercurial has been carefully " "designed to be both <emphasis>safe</emphasis> and <emphasis>efficient</" @@ -4111,7 +3188,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch04-concepts.xml:20 +#: ../en/ch03-concepts.xml:20 msgid "" "In this chapter, we'll initially cover the core concepts behind Mercurial's " "design, then continue to discuss some of the interesting details of its " @@ -4119,17 +3196,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch04-concepts.xml:25 +#: ../en/ch03-concepts.xml:25 msgid "Mercurial's historical record" msgstr "Mercurial 的历史记录" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:28 +#: ../en/ch03-concepts.xml:28 msgid "Tracking the history of a single file" msgstr "跟踪单一文件的历史" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:30 +#: ../en/ch03-concepts.xml:30 msgid "" "When Mercurial tracks modifications to a file, it stores the history of that " "file in a metadata object called a <emphasis>filelog</emphasis>. Each entry " @@ -4141,7 +3218,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:41 +#: ../en/ch03-concepts.xml:41 msgid "" "A file that is large, or has a lot of history, has its filelog stored in " "separate data (<quote><literal>.d</literal></quote> suffix) and index " @@ -4154,23 +3231,23 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:55 +#: ../en/ch03-concepts.xml:55 msgid "<imageobject><imagedata fileref=\"images/filelog.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:57 +#: ../en/ch03-concepts.xml:57 msgid "" "Relationships between files in working directory and filelogs in repository" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:65 +#: ../en/ch03-concepts.xml:65 msgid "Managing tracked files" msgstr "管理跟踪的文件" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:67 +#: ../en/ch03-concepts.xml:67 msgid "" "Mercurial uses a structure called a <emphasis>manifest</emphasis> to collect " "together information about the files that it tracks. Each entry in the " @@ -4180,12 +3257,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:77 +#: ../en/ch03-concepts.xml:77 msgid "Recording changeset information" msgstr "记录修改集信息" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:79 +#: ../en/ch03-concepts.xml:79 msgid "" "The <emphasis>changelog</emphasis> contains information about each " "changeset. Each revision records who committed a change, the changeset " @@ -4194,12 +3271,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:87 +#: ../en/ch03-concepts.xml:87 msgid "Relationships between revisions" msgstr "版本之间的关系" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:89 +#: ../en/ch03-concepts.xml:89 msgid "" "Within a changelog, a manifest, or a filelog, each revision stores a pointer " "to its immediate parent (or to its two parents, if it's a merge revision). " @@ -4209,7 +3286,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:96 +#: ../en/ch03-concepts.xml:96 msgid "" "For every changeset in a repository, there is exactly one revision stored in " "the changelog. Each revision of the changelog contains a pointer to a single " @@ -4220,17 +3297,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:107 +#: ../en/ch03-concepts.xml:107 msgid "<imageobject><imagedata fileref=\"images/metadata.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:109 +#: ../en/ch03-concepts.xml:109 msgid "Metadata relationships" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:114 +#: ../en/ch03-concepts.xml:114 msgid "" "As the illustration shows, there is <emphasis>not</emphasis> a <quote>one to " "one</quote> relationship between revisions in the changelog, manifest, or " @@ -4242,24 +3319,24 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch04-concepts.xml:127 +#: ../en/ch03-concepts.xml:127 msgid "Safe, efficient storage" msgstr "安全,高效的存储" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:129 +#: ../en/ch03-concepts.xml:129 msgid "" "The underpinnings of changelogs, manifests, and filelogs are provided by a " "single structure called the <emphasis>revlog</emphasis>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:134 +#: ../en/ch03-concepts.xml:134 msgid "Efficient storage" msgstr "高效存储" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:136 +#: ../en/ch03-concepts.xml:136 msgid "" "The revlog provides efficient storage of revisions using a <emphasis>delta</" "emphasis> mechanism. Instead of storing a complete copy of a file for each " @@ -4269,7 +3346,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:144 +#: ../en/ch03-concepts.xml:144 msgid "" "Some obsolete revision control systems can only work with deltas of text " "files. They must either store binary files as complete snapshots or encoded " @@ -4279,12 +3356,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:153 +#: ../en/ch03-concepts.xml:153 msgid "Safe operation" msgstr "安全操作" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:155 +#: ../en/ch03-concepts.xml:155 msgid "" "Mercurial only ever <emphasis>appends</emphasis> data to the end of a revlog " "file. It never modifies a section of a file after it has written it. This is " @@ -4293,7 +3370,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:161 +#: ../en/ch03-concepts.xml:161 msgid "" "In addition, Mercurial treats every write as part of a <emphasis>transaction</" "emphasis> that can span a number of files. A transaction is " @@ -4305,7 +3382,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:171 +#: ../en/ch03-concepts.xml:171 msgid "" "The fact that Mercurial only appends to files makes it easier to provide this " "transactional guarantee. The easier it is to do stuff like this, the more " @@ -4313,12 +3390,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:178 +#: ../en/ch03-concepts.xml:178 msgid "Fast retrieval" msgstr "快速检索" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:180 +#: ../en/ch03-concepts.xml:180 msgid "" "Mercurial cleverly avoids a pitfall common to all earlier revision control " "systems: the problem of <emphasis>inefficient retrieval</emphasis>. Most " @@ -4331,17 +3408,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:193 +#: ../en/ch03-concepts.xml:193 msgid "<imageobject><imagedata fileref=\"images/snapshot.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:195 +#: ../en/ch03-concepts.xml:195 msgid "Snapshot of a revlog, with incremental deltas" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:200 +#: ../en/ch03-concepts.xml:200 msgid "" "The innovation that Mercurial applies to this problem is simple but " "effective. Once the cumulative amount of delta information stored since the " @@ -4353,7 +3430,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:209 +#: ../en/ch03-concepts.xml:209 msgid "" "Figure <xref endterm=\"fig.concepts.snapshot.caption\" linkend=\"fig.concepts." "snapshot\"/> illustrates the idea. In an entry in a revlog's index file, " @@ -4362,12 +3439,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch04-concepts.xml:216 +#: ../en/ch03-concepts.xml:216 msgid "Aside: the influence of video compression" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch04-concepts.xml:218 +#: ../en/ch03-concepts.xml:218 msgid "" "If you're familiar with video compression or have ever watched a TV feed " "through a digital cable or satellite service, you may know that most video " @@ -4378,7 +3455,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch04-concepts.xml:227 +#: ../en/ch03-concepts.xml:227 msgid "" "Because it's possible for a video stream to <quote>drop out</quote> " "occasionally due to signal glitches, and to limit the accumulation of " @@ -4391,12 +3468,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:241 +#: ../en/ch03-concepts.xml:241 msgid "Identification and strong integrity" msgstr "鉴别和强完整性" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:243 +#: ../en/ch03-concepts.xml:243 msgid "" "Along with delta or snapshot information, a revlog entry contains a " "cryptographic hash of the data that it represents. This makes it difficult " @@ -4404,7 +3481,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:248 +#: ../en/ch03-concepts.xml:248 msgid "" "Hashes provide more than a mere check against corruption; they are used as " "the identifiers for revisions. The changeset identification hashes that you " @@ -4413,7 +3490,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:255 +#: ../en/ch03-concepts.xml:255 msgid "" "Mercurial verifies that hashes are correct when it retrieves file revisions " "and when it pulls changes from another repository. If it encounters an " @@ -4421,7 +3498,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:260 +#: ../en/ch03-concepts.xml:260 msgid "" "In addition to the effect it has on retrieval efficiency, Mercurial's use of " "periodic snapshots makes it more robust against partial data corruption. If " @@ -4432,12 +3509,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch04-concepts.xml:272 +#: ../en/ch03-concepts.xml:272 msgid "Revision history, branching, and merging" msgstr "修订历史,分支与合并" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:274 +#: ../en/ch03-concepts.xml:274 msgid "" "Every entry in a Mercurial revlog knows the identity of its immediate " "ancestor revision, usually referred to as its <emphasis>parent</emphasis>. " @@ -4448,7 +3525,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:282 +#: ../en/ch03-concepts.xml:282 msgid "" "In figure <xref endterm=\"fig.concepts.revlog.caption\" linkend=\"fig." "concepts.revlog\"/>, you can see an example of the conceptual structure of a " @@ -4457,7 +3534,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:289 +#: ../en/ch03-concepts.xml:289 msgid "" "The first revision in a revlog (at the bottom of the image) has the null ID " "in both of its parent slots. For a <quote>normal</quote> revision, its first " @@ -4469,29 +3546,29 @@ msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:300 +#: ../en/ch03-concepts.xml:300 msgid "<imageobject><imagedata fileref=\"images/revlog.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:302 +#: ../en/ch03-concepts.xml:302 msgid "Revision in revlog" msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch04-concepts.xml:309 +#: ../en/ch03-concepts.xml:309 msgid "The working directory" msgstr "工作目录" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:311 +#: ../en/ch03-concepts.xml:311 msgid "" "In the working directory, Mercurial stores a snapshot of the files from the " "repository as of a particular changeset." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:314 +#: ../en/ch03-concepts.xml:314 msgid "" "The working directory <quote>knows</quote> which changeset it contains. When " "you update the working directory to contain a particular changeset, Mercurial " @@ -4502,7 +3579,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:323 +#: ../en/ch03-concepts.xml:323 msgid "" "The <emphasis>dirstate</emphasis> contains Mercurial's knowledge of the " "working directory. This details which changeset the working directory is " @@ -4511,7 +3588,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:329 +#: ../en/ch03-concepts.xml:329 msgid "" "Just as a revision of a revlog has room for two parents, so that it can " "represent either a normal revision (with one parent) or a merge of two " @@ -4526,12 +3603,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:343 +#: ../en/ch03-concepts.xml:343 msgid "What happens when you commit" msgstr "当你提交时发生的事情" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:345 +#: ../en/ch03-concepts.xml:345 msgid "" "The dirstate stores parent information for more than just book-keeping " "purposes. Mercurial uses the parents of the dirstate as <emphasis>the " @@ -4539,17 +3616,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:352 +#: ../en/ch03-concepts.xml:352 msgid "<imageobject><imagedata fileref=\"images/wdir.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:354 +#: ../en/ch03-concepts.xml:354 msgid "The working directory can have two parents" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:359 +#: ../en/ch03-concepts.xml:359 msgid "" "Figure <xref endterm=\"fig.concepts.wdir.caption\" linkend=\"fig.concepts.wdir" "\"/> shows the normal state of the working directory, where it has a single " @@ -4558,19 +3635,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:368 +#: ../en/ch03-concepts.xml:368 msgid "" "<imageobject><imagedata fileref=\"images/wdir-after-commit.png\"/> </" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:371 +#: ../en/ch03-concepts.xml:371 msgid "The working directory gains new parents after a commit" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:376 +#: ../en/ch03-concepts.xml:376 msgid "" "It's useful to think of the working directory as <quote>the changeset I'm " "about to commit</quote>. Any files that you tell Mercurial that you've " @@ -4580,7 +3657,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:384 +#: ../en/ch03-concepts.xml:384 msgid "" "After a commit, Mercurial will update the parents of the working directory, " "so that the first parent is the ID of the new changeset, and the second is " @@ -4591,12 +3668,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:396 +#: ../en/ch03-concepts.xml:396 msgid "Creating a new head" msgstr "创建新顶点" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:398 +#: ../en/ch03-concepts.xml:398 msgid "" "It's perfectly normal to update the working directory to a changeset other " "than the current tip. For example, you might want to know what your project " @@ -4610,19 +3687,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:412 +#: ../en/ch03-concepts.xml:412 msgid "" "<imageobject><imagedata fileref=\"images/wdir-pre-branch.png\"/> </" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:415 +#: ../en/ch03-concepts.xml:415 msgid "The working directory, updated to an older changeset" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:420 +#: ../en/ch03-concepts.xml:420 msgid "" "Having updated the working directory to an older changeset, what happens if " "you make some changes, and then commit? Mercurial behaves in the same way as " @@ -4635,18 +3712,18 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:434 +#: ../en/ch03-concepts.xml:434 msgid "" "<imageobject><imagedata fileref=\"images/wdir-branch.png\"/> </imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:437 +#: ../en/ch03-concepts.xml:437 msgid "After a commit made while synced to an older changeset" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch04-concepts.xml:443 +#: ../en/ch03-concepts.xml:443 msgid "" "If you're new to Mercurial, you should keep in mind a common <quote>error</" "quote>, which is to use the <command role=\"hg-cmd\">hg pull</command> " @@ -4660,7 +3737,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch04-concepts.xml:455 +#: ../en/ch03-concepts.xml:455 msgid "" "I put the word <quote>error</quote> in quotes because all that you need to do " "to rectify this situation is <command role=\"hg-cmd\">hg merge</command>, " @@ -4671,12 +3748,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:467 +#: ../en/ch03-concepts.xml:467 msgid "Merging heads" msgstr "合并顶点" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:469 +#: ../en/ch03-concepts.xml:469 msgid "" "When you run the <command role=\"hg-cmd\">hg merge</command> command, " "Mercurial leaves the first parent of the working directory unchanged, and " @@ -4686,18 +3763,18 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch04-concepts.xml:478 +#: ../en/ch03-concepts.xml:478 msgid "" "<imageobject><imagedata fileref=\"images/wdir-merge.png\"/> </imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch04-concepts.xml:481 +#: ../en/ch03-concepts.xml:481 msgid "Merging two heads" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:486 +#: ../en/ch03-concepts.xml:486 msgid "" "Mercurial also has to modify the working directory, to merge the files " "managed in the two changesets. Simplified a little, the merging process goes " @@ -4705,33 +3782,33 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch04-concepts.xml:491 +#: ../en/ch03-concepts.xml:491 msgid "If neither changeset has modified a file, do nothing with that file." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch04-concepts.xml:494 +#: ../en/ch03-concepts.xml:494 msgid "" "If one changeset has modified a file, and the other hasn't, create the " "modified copy of the file in the working directory." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch04-concepts.xml:498 +#: ../en/ch03-concepts.xml:498 msgid "" "If one changeset has removed a file, and the other hasn't (or has also " "deleted it), delete the file from the working directory." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch04-concepts.xml:502 +#: ../en/ch03-concepts.xml:502 msgid "" "If one changeset has removed a file, but the other has modified the file, ask " "the user what to do: keep the modified file, or remove it?" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch04-concepts.xml:506 +#: ../en/ch03-concepts.xml:506 msgid "" "If both changesets have modified a file, invoke an external merge program to " "choose the new contents for the merged file. This may require input from the " @@ -4739,14 +3816,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch04-concepts.xml:511 +#: ../en/ch03-concepts.xml:511 msgid "" "If one changeset has modified a file, and the other has renamed or copied the " "file, make sure that the changes follow the new name of the file." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:515 +#: ../en/ch03-concepts.xml:515 msgid "" "There are more details&emdash;merging has plenty of corner cases&emdash;but " "these are the most common choices that are involved in a merge. As you can " @@ -4755,7 +3832,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:522 +#: ../en/ch03-concepts.xml:522 msgid "" "When you're thinking about what happens when you commit after a merge, once " "again the working directory is <quote>the changeset I'm about to commit</" @@ -4765,7 +3842,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:529 +#: ../en/ch03-concepts.xml:529 msgid "" "Mercurial lets you perform multiple merges, but you must commit the results " "of each individual merge as you go. This is necessary because Mercurial only " @@ -4776,12 +3853,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch04-concepts.xml:540 +#: ../en/ch03-concepts.xml:540 msgid "Other interesting design features" msgstr "其它有趣的设计特性" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch04-concepts.xml:542 +#: ../en/ch03-concepts.xml:542 msgid "" "In the sections above, I've tried to highlight some of the most important " "aspects of Mercurial's design, to illustrate that it pays careful attention " @@ -4794,12 +3871,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:553 +#: ../en/ch03-concepts.xml:553 msgid "Clever compression" msgstr "智能压缩" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:555 +#: ../en/ch03-concepts.xml:555 msgid "" "When appropriate, Mercurial will store both snapshots and deltas in " "compressed form. It does this by always <emphasis>trying to</emphasis> " @@ -4808,7 +3885,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:561 +#: ../en/ch03-concepts.xml:561 msgid "" "This means that Mercurial does <quote>the right thing</quote> when storing a " "file whose native form is compressed, such as a <literal>zip</literal> " @@ -4818,7 +3895,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:569 +#: ../en/ch03-concepts.xml:569 msgid "" "Deltas between revisions of a compressed file are usually larger than " "snapshots of the file, and Mercurial again does <quote>the right thing</" @@ -4828,12 +3905,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch04-concepts.xml:578 +#: ../en/ch03-concepts.xml:578 msgid "Network recompression" msgstr "网络重新压缩" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch04-concepts.xml:580 +#: ../en/ch03-concepts.xml:580 msgid "" "When storing revisions on disk, Mercurial uses the <quote>deflate</quote> " "compression algorithm (the same one used by the popular <literal>zip</" @@ -4843,7 +3920,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch04-concepts.xml:588 +#: ../en/ch03-concepts.xml:588 msgid "" "If the connection is over HTTP, Mercurial recompresses the entire stream of " "data using a compression algorithm that gives a better compression ratio (the " @@ -4855,7 +3932,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch04-concepts.xml:598 +#: ../en/ch03-concepts.xml:598 msgid "" "(If the connection is over <command>ssh</command>, Mercurial " "<emphasis>doesn't</emphasis> recompress the stream, because <command>ssh</" @@ -4863,12 +3940,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:606 +#: ../en/ch03-concepts.xml:606 msgid "Read/write ordering and atomicity" msgstr "读写顺序与原子性" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:608 +#: ../en/ch03-concepts.xml:608 msgid "" "Appending to files isn't the whole story when it comes to guaranteeing that a " "reader won't see a partial write. If you recall figure <xref endterm=\"fig." @@ -4878,7 +3955,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:616 +#: ../en/ch03-concepts.xml:616 msgid "" "A writer starts a transaction by writing filelog and manifest data, and " "doesn't write any changelog data until those are finished. A reader starts " @@ -4886,7 +3963,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:621 +#: ../en/ch03-concepts.xml:621 msgid "" "Since the writer has always finished writing filelog and manifest data before " "it writes to the changelog, a reader will never read a pointer to a partially " @@ -4895,12 +3972,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:629 +#: ../en/ch03-concepts.xml:629 msgid "Concurrent access" msgstr "并发访问" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:631 +#: ../en/ch03-concepts.xml:631 msgid "" "The read/write ordering and atomicity guarantees mean that Mercurial never " "needs to <emphasis>lock</emphasis> a repository when it's reading data, even " @@ -4911,7 +3988,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:640 +#: ../en/ch03-concepts.xml:640 msgid "" "The lockless nature of reading means that if you're sharing a repository on a " "multi-user system, you don't need to grant other local users permission to " @@ -4925,7 +4002,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:653 +#: ../en/ch03-concepts.xml:653 msgid "" "Mercurial uses locks to ensure that only one process can write to a " "repository at a time (the locking mechanism is safe even over filesystems " @@ -4939,12 +4016,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch04-concepts.xml:665 +#: ../en/ch03-concepts.xml:665 msgid "Safe dirstate access" msgstr "安全的目录状态访问" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch04-concepts.xml:667 +#: ../en/ch03-concepts.xml:667 msgid "" "As with revision data, Mercurial doesn't take a lock to read the dirstate " "file; it does acquire a lock to write it. To avoid the possibility of " @@ -4956,12 +4033,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:680 +#: ../en/ch03-concepts.xml:680 msgid "Avoiding seeks" msgstr "避免查找" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:682 +#: ../en/ch03-concepts.xml:682 msgid "" "Critical to Mercurial's performance is the avoidance of seeks of the disk " "head, since any seek is far more expensive than even a comparatively large " @@ -4969,7 +4046,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:686 +#: ../en/ch03-concepts.xml:686 msgid "" "This is why, for example, the dirstate is stored in a single file. If there " "were a dirstate file per directory that Mercurial tracked, the disk would " @@ -4978,7 +4055,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:692 +#: ../en/ch03-concepts.xml:692 msgid "" "Mercurial also uses a <quote>copy on write</quote> scheme when cloning a " "repository on local storage. Instead of copying every revlog file from the " @@ -4991,7 +4068,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:703 +#: ../en/ch03-concepts.xml:703 msgid "" "A few revision control developers have pointed out that this idea of making a " "complete private copy of a file is not very efficient in its use of storage. " @@ -5003,12 +4080,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch04-concepts.xml:715 +#: ../en/ch03-concepts.xml:715 msgid "Other contents of the dirstate" msgstr "目录状态的其它内容" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:717 +#: ../en/ch03-concepts.xml:717 msgid "" "Because Mercurial doesn't force you to tell it when you're modifying a file, " "it uses the dirstate to store some extra information so it can determine " @@ -5018,7 +4095,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:724 +#: ../en/ch03-concepts.xml:724 msgid "" "When you explicitly <command role=\"hg-cmd\">hg add</command>, <command role=" "\"hg-cmd\">hg remove</command>, <command role=\"hg-cmd\">hg rename</command> " @@ -5027,7 +4104,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch04-concepts.xml:731 +#: ../en/ch03-concepts.xml:731 msgid "" "When Mercurial is checking the states of files in the working directory, it " "first checks a file's modification time. If that has not changed, the file " @@ -5041,17 +4118,17 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch05-daily.xml:5 +#: ../en/ch04-daily.xml:5 msgid "Mercurial in daily use" msgstr "Mercurial 的日常使用" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch05-daily.xml:8 +#: ../en/ch04-daily.xml:8 msgid "Telling Mercurial which files to track" msgstr "告诉 Mercurial 要跟踪哪些文件" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:10 +#: ../en/ch04-daily.xml:10 msgid "" "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 " @@ -5061,7 +4138,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:17 +#: ../en/ch04-daily.xml:17 msgid "" "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 " @@ -5070,7 +4147,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:26 +#: ../en/ch04-daily.xml:26 msgid "" "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 " @@ -5084,7 +4161,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:38 +#: ../en/ch04-daily.xml:38 msgid "" "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 " @@ -5093,12 +4170,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:45 +#: ../en/ch04-daily.xml:45 msgid "Explicit versus implicit file naming" msgstr "明确与隐含文件命名" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:47 +#: ../en/ch04-daily.xml:47 msgid "" "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 " @@ -5107,7 +4184,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:54 +#: ../en/ch04-daily.xml:54 msgid "" "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</" @@ -5115,7 +4192,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:59 +#: ../en/ch04-daily.xml:59 msgid "" "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 " @@ -5123,7 +4200,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:64 +#: ../en/ch04-daily.xml:64 msgid "" "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 " @@ -5133,12 +4210,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:73 +#: ../en/ch04-daily.xml:73 msgid "Aside: Mercurial tracks files, not directories" msgstr "旁白: Mercurial 只跟踪文件,不跟踪目录" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:75 +#: ../en/ch04-daily.xml:75 msgid "" "Mercurial does not track directory information. Instead, it tracks the path " "to a file. Before creating a file, it first creates any missing directory " @@ -5149,7 +4226,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:84 +#: ../en/ch04-daily.xml:84 msgid "" "Empty directories are rarely useful, and there are unintrusive workarounds " "that you can use to achieve an appropriate effect. The developers of " @@ -5158,7 +4235,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:91 +#: ../en/ch04-daily.xml:91 msgid "" "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 " @@ -5169,20 +4246,20 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:102 +#: ../en/ch04-daily.xml:102 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch05-daily.xml:109 +#: ../en/ch04-daily.xml:109 msgid "How to stop tracking a file" msgstr "如何停止跟踪文件" # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:111 +#: ../en/ch04-daily.xml:111 msgid "" "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, " @@ -5192,7 +4269,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:120 +#: ../en/ch04-daily.xml:120 msgid "" "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 " @@ -5203,36 +4280,36 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:129 +#: ../en/ch04-daily.xml:129 msgid "Removing a file does not affect its history" msgstr "删除文件不影响历史" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:131 +#: ../en/ch04-daily.xml:131 msgid "It is important to understand that removing a file has only two effects." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch05-daily.xml:134 +#: ../en/ch04-daily.xml:134 msgid "It removes the current version of the file from the working directory." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch05-daily.xml:137 +#: ../en/ch04-daily.xml:137 msgid "" "It stops Mercurial from tracking changes to the file, from the time of the " "next commit." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:140 +#: ../en/ch04-daily.xml:140 msgid "" "Removing a file <emphasis>does not</emphasis> in any way alter the " "<emphasis>history</emphasis> of the file." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:143 +#: ../en/ch04-daily.xml:143 msgid "" "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, " @@ -5242,12 +4319,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:153 +#: ../en/ch04-daily.xml:153 msgid "Missing files" msgstr "丢失的文件" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:155 +#: ../en/ch04-daily.xml:155 msgid "" "Mercurial considers a file that you have deleted, but not used <command role=" "\"hg-cmd\">hg remove</command> to delete, to be <emphasis>missing</" @@ -5257,7 +4334,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:165 +#: ../en/ch04-daily.xml:165 msgid "" "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 " @@ -5267,7 +4344,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:175 +#: ../en/ch04-daily.xml:175 msgid "" "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 " @@ -5275,12 +4352,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:184 +#: ../en/ch04-daily.xml:184 msgid "Aside: why tell Mercurial explicitly to remove a file?" msgstr "旁白: 为什么要明确告诉 Mercurial 删除文件?" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:187 +#: ../en/ch04-daily.xml:187 msgid "" "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 " @@ -5291,12 +4368,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:198 +#: ../en/ch04-daily.xml:198 msgid "Useful shorthand&emdash;adding and removing files in one step" msgstr "有用的速记&emdash;一个步骤添加和删除文件" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:201 +#: ../en/ch04-daily.xml:201 msgid "" "Mercurial offers a combination command, <command role=\"hg-cmd\">hg " "addremove</command>, that adds untracked files and marks missing files as " @@ -5304,7 +4381,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:207 +#: ../en/ch04-daily.xml:207 msgid "" "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-" @@ -5312,12 +4389,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch05-daily.xml:217 +#: ../en/ch04-daily.xml:217 msgid "Copying files" msgstr "复制文件" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:219 +#: ../en/ch04-daily.xml:219 msgid "" "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, " @@ -5327,12 +4404,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:227 +#: ../en/ch04-daily.xml:227 msgid "The results of copying during a merge" msgstr "合并期间的复制结果" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:229 +#: ../en/ch04-daily.xml:229 msgid "" "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 " @@ -5340,28 +4417,28 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:236 +#: ../en/ch04-daily.xml:236 msgid "" "We need to do some work in parallel, so that we'll have something to merge. " "So let's clone our repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:242 +#: ../en/ch04-daily.xml:242 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:248 +#: ../en/ch04-daily.xml:248 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:254 +#: ../en/ch04-daily.xml:254 msgid "" "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 " @@ -5370,14 +4447,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:262 +#: ../en/ch04-daily.xml:262 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:268 +#: ../en/ch04-daily.xml:268 msgid "" "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, " @@ -5386,19 +4463,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:278 +#: ../en/ch04-daily.xml:278 msgid "Why should changes follow copies?" msgstr "为什么复制后需要后续修改?" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:280 +#: ../en/ch04-daily.xml:280 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:284 +#: ../en/ch04-daily.xml:284 msgid "" "First of all, remember that this propagation <emphasis>only</emphasis> " "happens when you merge. So if you <command role=\"hg-cmd\">hg copy</command> " @@ -5407,7 +4484,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:290 +#: ../en/ch04-daily.xml:290 msgid "" "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 " @@ -5415,7 +4492,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:295 +#: ../en/ch04-daily.xml:295 msgid "" "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 " @@ -5425,7 +4502,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:302 +#: ../en/ch04-daily.xml:302 msgid "" "If you pulled and merged my changes, and Mercurial <emphasis>didn't</" "emphasis> propagate changes across copies, your source file would now contain " @@ -5434,7 +4511,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:308 +#: ../en/ch04-daily.xml:308 msgid "" "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, " @@ -5443,7 +4520,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:314 +#: ../en/ch04-daily.xml:314 msgid "" "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 " @@ -5452,12 +4529,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:322 +#: ../en/ch04-daily.xml:322 msgid "How to make changes <emphasis>not</emphasis> follow a copy" msgstr "如何让复制后<emphasis>不</emphasis>修改?" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:325 +#: ../en/ch04-daily.xml:325 msgid "" "If, for some reason, you decide that this business of automatically " "propagating changes across copies is not for you, simply use your system's " @@ -5469,12 +4546,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:338 +#: ../en/ch04-daily.xml:338 msgid "Behaviour of the <command role=\"hg-cmd\">hg copy</command> command" msgstr "命令 <command role=\"hg-cmd\">hg copy</command> 的特性" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:341 +#: ../en/ch04-daily.xml:341 msgid "" "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 " @@ -5486,7 +4563,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:351 +#: ../en/ch04-daily.xml:351 msgid "" "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" @@ -5497,21 +4574,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:362 +#: ../en/ch04-daily.xml:362 msgid "" "If the destination is a directory, Mercurial copies its sources into that " "directory." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:367 +#: ../en/ch04-daily.xml:367 msgid "" "Copying a directory is recursive, and preserves the directory structure of " "the source." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:373 +#: ../en/ch04-daily.xml:373 msgid "" "If the source and destination are both directories, the source tree is " "recreated in the destination directory." @@ -5519,7 +4596,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:378 +#: ../en/ch04-daily.xml:378 msgid "" "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, " @@ -5528,12 +4605,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch05-daily.xml:389 +#: ../en/ch04-daily.xml:389 msgid "Renaming files" msgstr "改名文件" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:391 +#: ../en/ch04-daily.xml:391 msgid "" "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 " @@ -5543,7 +4620,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:399 +#: ../en/ch04-daily.xml:399 msgid "" "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 " @@ -5551,14 +4628,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:405 +#: ../en/ch04-daily.xml:405 msgid "" "The <command role=\"hg-cmd\">hg status</command> command shows the newly " "copied file as added, and the copied-from file as removed." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:411 +#: ../en/ch04-daily.xml:411 msgid "" "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=" @@ -5567,7 +4644,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:420 +#: ../en/ch04-daily.xml:420 msgid "" "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 " @@ -5578,19 +4655,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:430 +#: ../en/ch04-daily.xml:430 msgid "Renaming files and merging changes" msgstr "改名文件与合并修改" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:432 +#: ../en/ch04-daily.xml:432 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:436 +#: ../en/ch04-daily.xml:436 msgid "" "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 " @@ -5600,7 +4677,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:443 +#: ../en/ch04-daily.xml:443 msgid "" "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 " @@ -5610,12 +4687,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:452 +#: ../en/ch04-daily.xml:452 msgid "Divergent renames and merging" msgstr "改名与合并的分歧" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:454 +#: ../en/ch04-daily.xml:454 msgid "" "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 " @@ -5623,17 +4700,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:461 +#: ../en/ch04-daily.xml:461 msgid "Anne renames the file to <filename>bar</filename>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:465 +#: ../en/ch04-daily.xml:465 msgid "Meanwhile, Bob renames it to <filename>quux</filename>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:470 +#: ../en/ch04-daily.xml:470 msgid "" "I like to think of this as a conflict because each developer has expressed " "different intentions about what the file ought to be named." @@ -5641,7 +4718,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:474 +#: ../en/ch04-daily.xml:474 msgid "" "What do you think should happen when they merge their work? Mercurial's " "actual behaviour is that it always preserves <emphasis>both</emphasis> names " @@ -5649,19 +4726,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:481 +#: ../en/ch04-daily.xml:481 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:487 +#: ../en/ch04-daily.xml:487 msgid "Convergent renames and merging" msgstr "收敛改名与合并" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:489 +#: ../en/ch04-daily.xml:489 msgid "" "Another kind of rename conflict occurs when two people choose to rename " "different <emphasis>source</emphasis> files to the same " @@ -5670,12 +4747,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch05-daily.xml:497 +#: ../en/ch04-daily.xml:497 msgid "Other name-related corner cases" msgstr "其它名称相关的角落" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch05-daily.xml:499 +#: ../en/ch04-daily.xml:499 msgid "" "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 " @@ -5684,19 +4761,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch05-daily.xml:511 +#: ../en/ch04-daily.xml:511 msgid "Recovering from mistakes" msgstr "从错误恢复" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:513 +#: ../en/ch04-daily.xml:513 msgid "" "Mercurial has some useful commands that will help you to recover from some " "common mistakes." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:516 +#: ../en/ch04-daily.xml:516 msgid "" "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 " @@ -5708,7 +4785,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:526 +#: ../en/ch04-daily.xml:526 msgid "" "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 " @@ -5717,7 +4794,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch05-daily.xml:532 +#: ../en/ch04-daily.xml:532 msgid "" "For more information about the <command role=\"hg-cmd\">hg revert</command> " "command, and details about how to deal with changes you have already " @@ -5725,12 +4802,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch06-collab.xml:5 +#: ../en/ch05-collab.xml:5 msgid "Collaborating with other people" msgstr "团体协作" #. type: Content of: <book><chapter><para> -#: ../en/ch06-collab.xml:7 +#: ../en/ch05-collab.xml:7 msgid "" "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 " @@ -5739,19 +4816,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch06-collab.xml:14 +#: ../en/ch05-collab.xml:14 msgid "Mercurial's web interface" msgstr "Mercurial 的 web 接口" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:16 +#: ../en/ch05-collab.xml:16 msgid "" "Mercurial has a powerful web interface that provides several useful " "capabilities." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:19 +#: ../en/ch05-collab.xml:19 msgid "" "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, " @@ -5760,7 +4837,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:24 +#: ../en/ch05-collab.xml:24 msgid "" "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 " @@ -5772,7 +4849,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:34 +#: ../en/ch05-collab.xml:34 msgid "" "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 " @@ -5781,7 +4858,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:40 +#: ../en/ch05-collab.xml:40 msgid "" "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 " @@ -5790,7 +4867,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:45 +#: ../en/ch05-collab.xml:45 msgid "" "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=" @@ -5804,12 +4881,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch06-collab.xml:60 +#: ../en/ch05-collab.xml:60 msgid "Collaboration models" msgstr "协作模型" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:62 +#: ../en/ch05-collab.xml:62 msgid "" "With a suitably flexible tool, making decisions about workflow is much more " "of a social engineering challenge than a technical one. Mercurial imposes few " @@ -5819,12 +4896,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:70 +#: ../en/ch05-collab.xml:70 msgid "Factors to keep in mind" msgstr "要牢记的因素" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:72 +#: ../en/ch05-collab.xml:72 msgid "" "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. " @@ -5833,7 +4910,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:78 +#: ../en/ch05-collab.xml:78 msgid "" "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 " @@ -5846,7 +4923,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:88 +#: ../en/ch05-collab.xml:88 msgid "" "Don't sweep foreseeable social or technical problems under the rug. Whatever " "scheme you put into effect, you should plan for mistakes and problem " @@ -5860,12 +4937,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:102 +#: ../en/ch05-collab.xml:102 msgid "Informal anarchy" msgstr "无政府状态" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:104 +#: ../en/ch05-collab.xml:104 msgid "" "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 " @@ -5873,7 +4950,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:109 +#: ../en/ch05-collab.xml:109 msgid "" "As one example, many projects have a loose-knit group of collaborators who " "rarely physically meet each other. Some groups like to overcome the " @@ -5885,7 +4962,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:118 +#: ../en/ch05-collab.xml:118 msgid "" "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 " @@ -5900,7 +4977,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:132 +#: ../en/ch05-collab.xml:132 msgid "" "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 " @@ -5910,12 +4987,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:141 +#: ../en/ch05-collab.xml:141 msgid "A single central repository" msgstr "单一中央版本库" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:143 +#: ../en/ch05-collab.xml:143 msgid "" "For smaller projects migrating from a centralised revision control tool, " "perhaps the easiest way to get started is to have changes flow through a " @@ -5924,7 +5001,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:149 +#: ../en/ch05-collab.xml:149 msgid "" "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 " @@ -5933,7 +5010,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:154 +#: ../en/ch05-collab.xml:154 msgid "" "Under this model, it can still often make sense for people to pull changes " "directly from each other, without going through the central repository. " @@ -5946,7 +5023,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:165 +#: ../en/ch05-collab.xml:165 msgid "" "In this kind of scenario, people usually use the <command>ssh</command> " "protocol to securely push changes to the central repository, as documented in " @@ -5958,12 +5035,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:178 +#: ../en/ch05-collab.xml:178 msgid "Working with multiple branches" msgstr "使用多个分支工作" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:180 +#: ../en/ch05-collab.xml:180 msgid "" "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 " @@ -5977,7 +5054,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:193 +#: ../en/ch05-collab.xml:193 msgid "" "Mercurial is particularly well suited to managing a number of simultaneous, " "but not identical, branches. Each <quote>development direction</quote> can " @@ -5988,19 +5065,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:202 +#: ../en/ch05-collab.xml:202 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:208 +#: ../en/ch05-collab.xml:208 msgid "People clone it, make changes locally, test them, and push them back." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:211 +#: ../en/ch05-collab.xml:211 msgid "" "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 " @@ -6008,12 +5085,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:217 +#: ../en/ch05-collab.xml:217 msgid "Let's say some ongoing development occurs on the main branch." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:222 +#: ../en/ch05-collab.xml:222 msgid "" "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 " @@ -6023,7 +5100,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:230 +#: ../en/ch05-collab.xml:230 msgid "" "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, " @@ -6031,7 +5108,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:236 +#: ../en/ch05-collab.xml:236 msgid "" "Someone who needs to make a change to the stable branch can then clone " "<emphasis>that</emphasis> repository, make their changes, commit, and push " @@ -6039,7 +5116,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:242 +#: ../en/ch05-collab.xml:242 msgid "" "Because Mercurial repositories are independent, and Mercurial doesn't move " "changes around automatically, the stable and main branches are " @@ -6049,7 +5126,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:249 +#: ../en/ch05-collab.xml:249 msgid "" "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 " @@ -6058,7 +5135,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:257 +#: ../en/ch05-collab.xml:257 msgid "" "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 " @@ -6066,12 +5143,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch06-collab.xml:264 ../en/ch06-collab.xml:278 +#: ../en/ch05-collab.xml:264 ../en/ch05-collab.xml:278 msgid "Feature branches" msgstr "特性分支" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:266 +#: ../en/ch05-collab.xml:266 msgid "" "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 " @@ -6081,14 +5158,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch06-collab.xml:275 +#: ../en/ch05-collab.xml:275 msgid "" "<imageobject><imagedata fileref=\"images/feature-branches.png\"/> </" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:283 +#: ../en/ch05-collab.xml:283 msgid "" "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, " @@ -6096,12 +5173,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:290 +#: ../en/ch05-collab.xml:290 msgid "The release train" msgstr "发布列车" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:292 +#: ../en/ch05-collab.xml:292 msgid "" "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 " @@ -6109,7 +5186,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:297 +#: ../en/ch05-collab.xml:297 msgid "" "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 " @@ -6119,12 +5196,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:306 +#: ../en/ch05-collab.xml:306 msgid "The Linux kernel model" msgstr "Linux 内核模型" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:308 +#: ../en/ch05-collab.xml:308 msgid "" "The development of the Linux kernel has a shallow hierarchical structure, " "surrounded by a cloud of apparent chaos. Because most Linux developers use " @@ -6134,7 +5211,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:316 +#: ../en/ch05-collab.xml:316 msgid "" "At the center of the community sits Linus Torvalds, the creator of Linux. He " "publishes a single source repository that is considered the " @@ -6144,7 +5221,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:323 +#: ../en/ch05-collab.xml:323 msgid "" "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 " @@ -6158,7 +5235,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:335 +#: ../en/ch05-collab.xml:335 msgid "" "Individual lieutenants have their own approaches to reviewing, accepting, and " "publishing changes; and for deciding when to feed them to Linus. In " @@ -6171,7 +5248,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:346 +#: ../en/ch05-collab.xml:346 msgid "" "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 " @@ -6181,7 +5258,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:353 +#: ../en/ch05-collab.xml:353 msgid "" "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 " @@ -6195,7 +5272,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:364 +#: ../en/ch05-collab.xml:364 msgid "" "Reputation and acclaim don't necessarily cross subsystem or <quote>people</" "quote> boundaries. If you're a respected but specialised storage hacker, and " @@ -6204,7 +5281,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:371 +#: ../en/ch05-collab.xml:371 msgid "" "To people who come from more orderly project backgrounds, the comparatively " "chaotic Linux kernel development process often seems completely insane. It's " @@ -6214,12 +5291,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:381 +#: ../en/ch05-collab.xml:381 msgid "Pull-only versus shared-push collaboration" msgstr "只读与共享写协作" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:383 +#: ../en/ch05-collab.xml:383 msgid "" "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 " @@ -6228,7 +5305,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:389 +#: ../en/ch05-collab.xml:389 msgid "" "Typically, the backers of the shared-push model use tools that actively " "enforce this approach. If you're using a centralised revision control tool " @@ -6239,7 +5316,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:397 +#: ../en/ch05-collab.xml:397 msgid "" "A good distributed revision control tool, such as Mercurial, will support " "both models. You and your collaborators can then structure how you work " @@ -6248,12 +5325,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:405 +#: ../en/ch05-collab.xml:405 msgid "Where collaboration meets branch management" msgstr "协作与分支管理" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:407 +#: ../en/ch05-collab.xml:407 msgid "" "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 " @@ -6265,24 +5342,24 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch06-collab.xml:419 +#: ../en/ch05-collab.xml:419 msgid "The technical side of sharing" msgstr "共享的技术因素" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:421 +#: ../en/ch05-collab.xml:421 msgid "" "The remainder of this chapter is devoted to the question of serving data to " "your collaborators." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch06-collab.xml:426 +#: ../en/ch05-collab.xml:426 msgid "Informal sharing with <command role=\"hg-cmd\">hg serve</command>" msgstr "使用 <command role=\"hg-cmd\">hg serve</command> 进行非正式共享" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:429 +#: ../en/ch05-collab.xml:429 msgid "" "Mercurial's <command role=\"hg-cmd\">hg serve</command> command is " "wonderfully suited to small, tight-knit, and fast-paced group environments. " @@ -6291,7 +5368,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:434 +#: ../en/ch05-collab.xml:434 msgid "" "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 " @@ -6304,21 +5381,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:445 +#: ../en/ch05-collab.xml:445 msgid "" "The <command role=\"hg-cmd\">hg serve</command> command is <emphasis>not</" "emphasis> a general-purpose web server. It can do only two things:" msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:449 +#: ../en/ch05-collab.xml:449 msgid "" "Allow people to browse the history of the repository it's serving, from their " "normal web browsers." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:453 +#: ../en/ch05-collab.xml:453 msgid "" "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 " @@ -6326,7 +5403,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:458 +#: ../en/ch05-collab.xml:458 msgid "" "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-" @@ -6334,7 +5411,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:462 +#: ../en/ch05-collab.xml:462 msgid "" "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 " @@ -6345,12 +5422,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:472 +#: ../en/ch05-collab.xml:472 msgid "A few things to keep in mind" msgstr "要牢记的几件事" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:474 +#: ../en/ch05-collab.xml:474 msgid "" "Because it provides unauthenticated read access to all clients, you should " "only use <command role=\"hg-cmd\">hg serve</command> in an environment where " @@ -6359,7 +5436,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:480 +#: ../en/ch05-collab.xml:480 msgid "" "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 " @@ -6370,7 +5447,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:489 +#: ../en/ch05-collab.xml:489 msgid "" "By default, <command role=\"hg-cmd\">hg serve</command> listens for incoming " "connections on port 8000. If another process is already listening on the " @@ -6379,7 +5456,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:495 +#: ../en/ch05-collab.xml:495 msgid "" "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 " @@ -6389,12 +5466,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch06-collab.xml:505 +#: ../en/ch05-collab.xml:505 msgid "Using the Secure Shell (ssh) protocol" msgstr "使用 ssh 协议" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:507 +#: ../en/ch05-collab.xml:507 msgid "" "You can pull and push changes securely over a network connection using the " "Secure Shell (<literal>ssh</literal>) protocol. To use this successfully, " @@ -6403,7 +5480,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:512 +#: ../en/ch05-collab.xml:512 msgid "" "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 " @@ -6412,31 +5489,31 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:518 +#: ../en/ch05-collab.xml:518 msgid "" "(If you <emphasis>are</emphasis> familiar with ssh, you'll probably find some " "of the material that follows to be elementary in nature.)" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:523 +#: ../en/ch05-collab.xml:523 msgid "How to read and write ssh URLs" msgstr "如何读写 ssh 路径" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:525 +#: ../en/ch05-collab.xml:525 msgid "An ssh URL tends to look like this:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:528 +#: ../en/ch05-collab.xml:528 msgid "" "The <quote><literal>ssh://</literal></quote> part tells Mercurial to use the " "ssh protocol." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:531 +#: ../en/ch05-collab.xml:531 msgid "" "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 " @@ -6444,28 +5521,28 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:536 +#: ../en/ch05-collab.xml:536 msgid "" "The <quote><literal>hg.serpentine.com</literal></quote> gives the hostname of " "the server to log into." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:540 +#: ../en/ch05-collab.xml:540 msgid "" "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." +"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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:545 +#: ../en/ch05-collab.xml:545 msgid "" "The remainder of the URL is the local path to the repository on the server." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:549 +#: ../en/ch05-collab.xml:549 msgid "" "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 " @@ -6475,7 +5552,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:556 +#: ../en/ch05-collab.xml:556 msgid "" "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 " @@ -6486,7 +5563,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:565 +#: ../en/ch05-collab.xml:565 msgid "" "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 " @@ -6494,19 +5571,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:571 +#: ../en/ch05-collab.xml:571 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:578 +#: ../en/ch05-collab.xml:578 msgid "Finding an ssh client for your system" msgstr "为你的系统寻找 ssh 客户端" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:580 +#: ../en/ch05-collab.xml:580 msgid "" "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 " @@ -6517,50 +5594,50 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:588 +#: ../en/ch05-collab.xml:588 msgid "" "On Windows, you'll first need to download a suitable ssh client. There are " "two alternatives." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:591 +#: ../en/ch05-collab.xml:591 msgid "" "Simon Tatham's excellent PuTTY package <citation>web:putty</citation> " "provides a complete suite of ssh client commands." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:595 +#: ../en/ch05-collab.xml:595 msgid "" "If you have a high tolerance for pain, you can use the Cygwin port of OpenSSH." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:598 -msgid "" -"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." +#: ../en/ch05-collab.xml:598 +msgid "" +"In either case, you'll need to edit your <filename role=\"special\">hg.ini</" +"filename> 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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch06-collab.xml:607 +#: ../en/ch05-collab.xml:608 msgid "" "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)." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:616 +"in <filename class=\"directory\">C:\\Program Files</filename> is probably not " +"a good idea)." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch05-collab.xml:617 msgid "Generating a key pair" msgstr "产生密钥对" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:618 +#: ../en/ch05-collab.xml:619 msgid "" "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, " @@ -6570,7 +5647,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:626 +#: ../en/ch05-collab.xml:627 msgid "" "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 " @@ -6579,7 +5656,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:632 +#: ../en/ch05-collab.xml:633 msgid "" "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 " @@ -6590,7 +5667,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:641 +#: ../en/ch05-collab.xml:642 msgid "" "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 " @@ -6600,12 +5677,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:650 +#: ../en/ch05-collab.xml:651 msgid "Using an authentication agent" msgstr "使用认证代理" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:652 +#: ../en/ch05-collab.xml:653 msgid "" "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 " @@ -6617,7 +5694,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:661 +#: ../en/ch05-collab.xml:662 msgid "" "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 " @@ -6627,7 +5704,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:668 +#: ../en/ch05-collab.xml:669 msgid "" "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 " @@ -6638,12 +5715,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:679 +#: ../en/ch05-collab.xml:680 msgid "Configuring the server side properly" msgstr "正确配置服务器端" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:681 +#: ../en/ch05-collab.xml:682 msgid "" "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 " @@ -6653,7 +5730,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:689 +#: ../en/ch05-collab.xml:690 msgid "" "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</" @@ -6666,7 +5743,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:700 +#: ../en/ch05-collab.xml:701 msgid "" "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 " @@ -6675,7 +5752,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:707 +#: ../en/ch05-collab.xml:708 msgid "" "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 " @@ -6683,7 +5760,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:712 +#: ../en/ch05-collab.xml:713 msgid "" "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 " @@ -6691,7 +5768,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:717 +#: ../en/ch05-collab.xml:718 msgid "" "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 " @@ -6699,7 +5776,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:722 +#: ../en/ch05-collab.xml:723 msgid "" "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 " @@ -6711,7 +5788,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:732 +#: ../en/ch05-collab.xml:733 msgid "" "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 " @@ -6720,26 +5797,26 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:738 +#: ../en/ch05-collab.xml:739 msgid "" "You might have forgotten to use <command>ssh-add</command> or " "<command>pageant</command> to store the passphrase." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:742 +#: ../en/ch05-collab.xml:743 msgid "You might have stored the passphrase for the wrong key." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:745 +#: ../en/ch05-collab.xml:746 msgid "" "If you're being prompted for the remote user's password, there are another " "few possible problems to check." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:748 +#: ../en/ch05-collab.xml:749 msgid "" "Either the user's home directory or their <filename role=\"special\" class=" "\"directory\">.ssh</filename> directory might have excessively liberal " @@ -6750,7 +5827,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:757 +#: ../en/ch05-collab.xml:758 msgid "" "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, " @@ -6758,7 +5835,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:764 +#: ../en/ch05-collab.xml:765 msgid "" "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 " @@ -6766,7 +5843,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:769 +#: ../en/ch05-collab.xml:770 msgid "" "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 " @@ -6781,7 +5858,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:783 +#: ../en/ch05-collab.xml:784 msgid "" "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 " @@ -6789,7 +5866,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:788 +#: ../en/ch05-collab.xml:791 msgid "" "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 " @@ -6799,21 +5876,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:795 +#: ../en/ch05-collab.xml:798 msgid "" "Is Mercurial really installed on the server at all? I know this sounds " "trivial, but it's worth checking!" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:799 +#: ../en/ch05-collab.xml:802 msgid "" "Maybe your shell's search path (usually set via the <envar>PATH</envar> " "environment variable) is simply misconfigured." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:803 +#: ../en/ch05-collab.xml:806 msgid "" "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 " @@ -6822,7 +5899,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:810 +#: ../en/ch05-collab.xml:813 msgid "" "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 " @@ -6830,7 +5907,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:817 +#: ../en/ch05-collab.xml:820 msgid "" "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 " @@ -6841,12 +5918,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:827 +#: ../en/ch05-collab.xml:830 msgid "Using compression with ssh" msgstr "通过 ssh 使用压缩" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:829 +#: ../en/ch05-collab.xml:832 msgid "" "Mercurial does not compress data when it uses the ssh protocol, because the " "ssh protocol can transparently compress data. However, the default behaviour " @@ -6854,7 +5931,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:834 +#: ../en/ch05-collab.xml:837 msgid "" "Over any network other than a fast LAN (even a wireless network), using " "compression is likely to significantly speed up Mercurial's network " @@ -6864,16 +5941,16 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:841 +#: ../en/ch05-collab.xml:844 msgid "" "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 " +"easily edit your <filename role=\"special\">~/.hgrc</filename> to enable " "compression for all of Mercurial's uses of the ssh protocol." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:848 +#: ../en/ch05-collab.xml:852 msgid "" "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 " @@ -6882,7 +5959,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:855 +#: ../en/ch05-collab.xml:860 msgid "" "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>-" @@ -6892,19 +5969,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch06-collab.xml:866 +#: ../en/ch05-collab.xml:871 msgid "Serving over HTTP using CGI" msgstr "使用 CGI 通过 HTTP 提供服务" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:868 +#: ../en/ch05-collab.xml:873 msgid "" "Depending on how ambitious you are, configuring Mercurial's CGI interface can " "take anything from a few moments to several hours." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch06-collab.xml:872 +#: ../en/ch05-collab.xml:877 msgid "" "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 " @@ -6912,7 +5989,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><note><para> -#: ../en/ch06-collab.xml:878 +#: ../en/ch05-collab.xml:883 msgid "" "Configuring a web server is a complex, fiddly, and highly system-dependent " "activity. I can't possibly give you instructions that will cover anything " @@ -6922,33 +5999,33 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:888 +#: ../en/ch05-collab.xml:893 msgid "Web server configuration checklist" msgstr "Web 服务器配置检查表" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:890 +#: ../en/ch05-collab.xml:895 msgid "" "Before you continue, do take a few moments to check a few aspects of your " "system's setup." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:894 +#: ../en/ch05-collab.xml:899 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:898 +#: ../en/ch05-collab.xml:903 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch06-collab.xml:902 +#: ../en/ch05-collab.xml:907 msgid "" "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 " @@ -6956,7 +6033,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:908 +#: ../en/ch05-collab.xml:913 msgid "" "If you don't have a web server installed, and don't have substantial " "experience configuring Apache, you should consider using the " @@ -6969,22 +6046,22 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:921 +#: ../en/ch05-collab.xml:926 msgid "Basic CGI configuration" msgstr "基本 CGI 配置" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:923 +#: ../en/ch05-collab.xml:928 msgid "" "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}." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:932 +"form <literal>http://www.example.com/username/foo</literal>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch05-collab.xml:936 msgid "" "To get started, find the <filename role=\"special\">hgweb.cgi</filename> " "script that should be present in your Mercurial installation. If you can't " @@ -6994,14 +6071,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:939 ../en/ch06-collab.xml:1112 +#: ../en/ch05-collab.xml:943 ../en/ch05-collab.xml:1112 msgid "" "You'll need to copy this script into your <filename class=\"directory" "\">public_html</filename> directory, and ensure that it's executable." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:944 +#: ../en/ch05-collab.xml:948 msgid "" "The <literal>755</literal> argument to <command>chmod</command> is a little " "more general than just making the script executable: it ensures that the " @@ -7014,12 +6091,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch06-collab.xml:958 +#: ../en/ch05-collab.xml:962 msgid "What could <emphasis>possibly</emphasis> go wrong?" msgstr "什么<emphasis>可能</emphasis>会出错?" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:961 +#: ../en/ch05-collab.xml:965 msgid "" "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://" @@ -7033,7 +6110,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:975 +#: ../en/ch05-collab.xml:979 msgid "" "Your web server may have per-user directories disabled. If you're using " "Apache, search your config file for a <literal>UserDir</literal> directive. " @@ -7046,7 +6123,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:986 +#: ../en/ch05-collab.xml:990 msgid "" "Your file access permissions may be too restrictive. The web server must be " "able to traverse your home directory and directories under your <filename " @@ -7056,7 +6133,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:996 +#: ../en/ch05-collab.xml:1000 msgid "" "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 " @@ -7066,15 +6143,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1003 +#: ../en/ch05-collab.xml:1007 msgid "" "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." msgstr "" -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1014 +#. type: CDATA +#: ../en/ch05-collab.xml:1012 +#, no-wrap +msgid "&ch06-apache-config.lst;]]" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch05-collab.xml:1014 msgid "" "If you find a similar-looking <literal>Directory</literal> group in your " "Apache configuration, the directive to look at inside it is <literal>Options</" @@ -7083,7 +6166,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1021 +#: ../en/ch05-collab.xml:1021 msgid "" "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 " @@ -7091,7 +6174,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1027 +#: ../en/ch05-collab.xml:1027 msgid "" "The next possibility is that you might be served with a colourful Python " "backtrace claiming that it can't import a <literal>mercurial</literal>-" @@ -7106,7 +6189,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1041 +#: ../en/ch05-collab.xml:1041 msgid "" "Finally, you are <emphasis>certain</emphasis> to by served with another " "colourful Python backtrace: this one will complain that it can't find " @@ -7117,19 +6200,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1051 +#: ../en/ch05-collab.xml:1051 msgid "" "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!" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch06-collab.xml:1057 +#: ../en/ch05-collab.xml:1057 msgid "Configuring lighttpd" msgstr "配置 lighttpd" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1059 +#: ../en/ch05-collab.xml:1059 msgid "" "To be exhaustive in my experiments, I tried configuring the increasingly " "popular <literal>lighttpd</literal> web server to serve the same repository " @@ -7140,7 +6223,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1069 +#: ../en/ch05-collab.xml:1069 msgid "" "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 " @@ -7152,7 +6235,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1081 +#: ../en/ch05-collab.xml:1081 msgid "" "With this done, <literal>lighttpd</literal> ran immediately for me. If I had " "configured <literal>lighttpd</literal> before Apache, I'd almost certainly " @@ -7163,12 +6246,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:1094 +#: ../en/ch05-collab.xml:1094 msgid "Sharing multiple repositories with one CGI script" msgstr "使用一个 CGI 脚本共享多个版本库" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1096 +#: ../en/ch05-collab.xml:1096 msgid "" "The <filename role=\"special\">hgweb.cgi</filename> script only lets you " "publish a single repository, which is an annoying restriction. If you want " @@ -7178,7 +6261,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1104 +#: ../en/ch05-collab.xml:1104 msgid "" "The procedure to configure <filename role=\"special\">hgwebdir.cgi</filename> " "is only a little more involved than for <filename role=\"special\">hgweb.cgi</" @@ -7189,7 +6272,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1117 +#: ../en/ch05-collab.xml:1117 msgid "" "With basic configuration out of the way, try to visit <ulink url=\"http://" "myhostname/ myuser/hgwebdir.cgi\">http://myhostname/ myuser/hgwebdir.cgi</" @@ -7199,7 +6282,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1126 +#: ../en/ch05-collab.xml:1126 msgid "" "The <filename role=\"special\">hgwebdir.cgi</filename> script relies on an " "external configuration file. By default, it searches for a file named " @@ -7211,7 +6294,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1136 +#: ../en/ch05-collab.xml:1136 msgid "" "The easiest way to configure <filename role=\"special\">hgwebdir.cgi</" "filename> is with a section named <literal>collections</literal>. This will " @@ -7220,7 +6303,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1144 +#: ../en/ch05-collab.xml:1144 msgid "" "Mercurial interprets this by looking at the directory name on the " "<emphasis>right</emphasis> hand side of the <quote><literal>=</literal></" @@ -7232,7 +6315,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1153 +#: ../en/ch05-collab.xml:1153 msgid "" "Given the example above, if we have a repository whose local path is " "<filename class=\"directory\">/my/root/this/repo</filename>, the CGI script " @@ -7246,7 +6329,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1167 +#: ../en/ch05-collab.xml:1167 msgid "" "If we replace <filename class=\"directory\">/my/root</filename> on the left " "hand side of this example with <filename class=\"directory\">/my</filename>, " @@ -7257,7 +6340,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1177 +#: ../en/ch05-collab.xml:1177 msgid "" "The <filename role=\"special\">hgwebdir.cgi</filename> script will " "recursively search each directory listed in the <literal>collections</" @@ -7266,7 +6349,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1183 +#: ../en/ch05-collab.xml:1183 msgid "" "The <literal>collections</literal> mechanism makes it easy to publish many " "repositories in a <quote>fire and forget</quote> manner. You only need to " @@ -7277,12 +6360,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch06-collab.xml:1193 +#: ../en/ch05-collab.xml:1193 msgid "Explicitly specifying which repositories to publish" msgstr "明确指出要发布的版本库" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1196 +#: ../en/ch05-collab.xml:1196 msgid "" "In addition to the <literal>collections</literal> mechanism, the <filename " "role=\"special\">hgwebdir.cgi</filename> script allows you to publish a " @@ -7291,7 +6374,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1204 +#: ../en/ch05-collab.xml:1205 msgid "" "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 " @@ -7301,7 +6384,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1211 +#: ../en/ch05-collab.xml:1212 msgid "" "If you wish, you can use both the <literal>collections</literal> and " "<literal>paths</literal> mechanisms simultaneously in a single configuration " @@ -7309,7 +6392,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><note><para> -#: ../en/ch06-collab.xml:1217 +#: ../en/ch05-collab.xml:1218 msgid "" "If multiple repositories have the same virtual path, <filename role=\"special" "\">hgwebdir.cgi</filename> will not report an error. Instead, it will behave " @@ -7317,12 +6400,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:1226 +#: ../en/ch05-collab.xml:1227 msgid "Downloading source archives" msgstr "下载源代码档案包" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1228 +#: ../en/ch05-collab.xml:1229 msgid "" "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 " @@ -7330,21 +6413,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1233 +#: ../en/ch05-collab.xml:1234 msgid "" "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</" +"\"rc-web\">web</literal> section of your <filename role=\"special\">~/.hgrc</" "filename>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch06-collab.xml:1241 +#: ../en/ch05-collab.xml:1242 msgid "Web configuration options" msgstr "Web 配置选项" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1243 +#: ../en/ch05-collab.xml:1244 msgid "" "Mercurial's web interfaces (the <command role=\"hg-cmd\">hg serve</command> " "command, and the <filename role=\"special\">hgweb.cgi</filename> and " @@ -7354,7 +6437,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1251 +#: ../en/ch05-collab.xml:1252 msgid "" "<envar role=\"rc-item-web\">allow_archive</envar>: Determines which (if any) " "archive download mechanisms Mercurial supports. If you enable this feature, " @@ -7364,7 +6447,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1260 +#: ../en/ch05-collab.xml:1261 msgid "" "<literal>bz2</literal>: A <command>tar</command> archive, compressed using " "<literal>bzip2</literal> compression. This has the best compression ratio, " @@ -7372,14 +6455,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1266 +#: ../en/ch05-collab.xml:1267 msgid "" "<literal>gz</literal>: A <command>tar</command> archive, compressed using " "<literal>gzip</literal> compression." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1270 +#: ../en/ch05-collab.xml:1271 msgid "" "<literal>zip</literal>: A <command>zip</command> archive, compressed using " "LZW compression. This format has the worst compression ratio, but is widely " @@ -7387,7 +6470,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1276 +#: ../en/ch05-collab.xml:1277 msgid "" "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 " @@ -7395,7 +6478,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1283 +#: ../en/ch05-collab.xml:1284 msgid "" "<envar role=\"rc-item-web\">allowpull</envar>: Boolean. Determines whether " "the web interface allows remote users to <command role=\"hg-cmd\">hg pull</" @@ -7405,33 +6488,33 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1292 +#: ../en/ch05-collab.xml:1293 msgid "" "<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 " +"use in a global <filename role=\"special\">~/.hgrc</filename> if every " "repository has a single maintainer." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1303 +#: ../en/ch05-collab.xml:1304 msgid "" "<envar role=\"rc-item-web\">maxchanges</envar>: Integer. The default maximum " "number of changesets to display in a single page of output." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1307 +#: ../en/ch05-collab.xml:1308 msgid "" "<envar role=\"rc-item-web\">maxfiles</envar>: Integer. The default maximum " "number of modified files to display in a single page of output." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1311 +#: ../en/ch05-collab.xml:1312 msgid "" "<envar role=\"rc-item-web\">stripes</envar>: Integer. If the web interface " "displays alternating <quote>stripes</quote> to make it easier to visually " @@ -7440,7 +6523,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1317 +#: ../en/ch05-collab.xml:1318 msgid "" "<envar role=\"rc-item-web\">style</envar>: Controls the template Mercurial " "uses to display the web interface. Mercurial ships with two web templates, " @@ -7451,7 +6534,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1328 +#: ../en/ch05-collab.xml:1330 msgid "" "<envar role=\"rc-item-web\">templates</envar>: Path. The directory in which " "to search for template files. By default, Mercurial searches in the " @@ -7459,32 +6542,32 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch06-collab.xml:1333 +#: ../en/ch05-collab.xml:1335 msgid "" "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 " +"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>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch06-collab.xml:1344 +#: ../en/ch05-collab.xml:1346 msgid "Options specific to an individual repository" msgstr "针对单个版本库的选项" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1346 +#: ../en/ch05-collab.xml:1348 msgid "" "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>, rather than a user's or global <filename role=\"special\">~/.hgrc</" "filename>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1352 +#: ../en/ch05-collab.xml:1353 msgid "" "<envar role=\"rc-item-web\">description</envar>: String. A free-form (but " "preferably brief) string that describes the contents or purpose of the " @@ -7492,7 +6575,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1357 +#: ../en/ch05-collab.xml:1358 msgid "" "<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 " @@ -7500,21 +6583,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch06-collab.xml:1365 +#: ../en/ch05-collab.xml:1366 msgid "" "Options specific to the <command role=\"hg-cmd\">hg serve</command> command" msgstr "命令 <command role=\"hg-cmd\">hg serve</command> 的选项" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1368 +#: ../en/ch05-collab.xml:1369 msgid "" "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." +"<filename role=\"special\">~/.hgrc</filename> file are only for use with the " +"<command role=\"hg-cmd\">hg serve</command> command." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1374 +#: ../en/ch05-collab.xml:1375 msgid "" "<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 " @@ -7524,7 +6607,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1382 +#: ../en/ch05-collab.xml:1383 msgid "" "<envar role=\"rc-item-web\">address</envar>: String. The local address on " "which the server should listen for incoming connections. By default, the " @@ -7532,7 +6615,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1387 +#: ../en/ch05-collab.xml:1388 msgid "" "<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 " @@ -7541,30 +6624,31 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1393 +#: ../en/ch05-collab.xml:1394 msgid "" "<envar role=\"rc-item-web\">ipv6</envar>: Boolean. Whether to use the IPv6 " "protocol. By default, IPv6 is not used." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch06-collab.xml:1397 +#: ../en/ch05-collab.xml:1398 msgid "" "<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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch06-collab.xml:1404 -msgid "" -"Choosing the right <filename role=\"special\"> /.hgrc</filename>\\ file to " -"add <literal role=\"rc-web\">web</literal> items to" +#: ../en/ch05-collab.xml:1405 +#, fuzzy +msgid "" +"Choosing the right <filename role=\"special\">~/.hgrc</filename> file to add " +"<literal role=\"rc-web\">web</literal> items to" msgstr "" "选择正确的 <filename role=\"special\"> /.hgrc</filename> 文件增加到 <literal " "role=\"rc-web\">web</literal> 条目" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1408 +#: ../en/ch05-collab.xml:1409 msgid "" "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 " @@ -7573,38 +6657,37 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch06-collab.xml:1415 +#: ../en/ch05-collab.xml:1416 msgid "" "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</" +"<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 " +"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." +"settings to a system-wide <filename role=\"special\">~/.hgrc</filename> file." msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch07-filenames.xml:5 +#: ../en/ch06-filenames.xml:5 msgid "File names and pattern matching" msgstr "文件名称与模式匹配" #. type: Content of: <book><chapter><para> -#: ../en/ch07-filenames.xml:7 +#: ../en/ch06-filenames.xml:7 msgid "" "Mercurial provides mechanisms that let you work with file names in a " "consistent and expressive way." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch07-filenames.xml:11 +#: ../en/ch06-filenames.xml:11 msgid "Simple file naming" msgstr "简单文件名称" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:13 +#: ../en/ch06-filenames.xml:13 msgid "" "Mercurial uses a unified piece of machinery <quote>under the hood</quote> to " "handle file names. Every command behaves uniformly with respect to file " @@ -7612,7 +6695,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:18 +#: ../en/ch06-filenames.xml:18 msgid "" "If you explicitly name real files on the command line, Mercurial works with " "exactly those files, as you would expect. &interaction.filenames.files;" @@ -7620,7 +6703,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:22 +#: ../en/ch06-filenames.xml:22 msgid "" "When you provide a directory name, Mercurial will interpret this as " "<quote>operate on every file in this directory and its subdirectories</" @@ -7630,12 +6713,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch07-filenames.xml:33 +#: ../en/ch06-filenames.xml:33 msgid "Running commands without any file names" msgstr "不提供文件名称的执行命令" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:35 +#: ../en/ch06-filenames.xml:35 msgid "" "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 " @@ -7645,14 +6728,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch07-filenames.xml:42 +#: ../en/ch06-filenames.xml:42 msgid "" "Most commands will operate on the entire working directory. This is what the " "<command role=\"hg-cmd\">hg add</command> command does, for example." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch07-filenames.xml:46 +#: ../en/ch06-filenames.xml:46 msgid "" "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 " @@ -7662,7 +6745,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:54 +#: ../en/ch06-filenames.xml:54 msgid "" "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 " @@ -7672,7 +6755,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:62 +#: ../en/ch06-filenames.xml:62 msgid "" "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. " @@ -7685,12 +6768,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch07-filenames.xml:76 +#: ../en/ch06-filenames.xml:76 msgid "Telling you what's going on" msgstr "告诉你正在做什么" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:78 +#: ../en/ch06-filenames.xml:78 msgid "" "The <command role=\"hg-cmd\">hg add</command> example in the preceding " "section illustrates something else that's helpful about Mercurial commands. " @@ -7700,7 +6783,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:85 +#: ../en/ch06-filenames.xml:85 msgid "" "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 " @@ -7710,7 +6793,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:92 +#: ../en/ch06-filenames.xml:92 msgid "" "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 " @@ -7719,12 +6802,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch07-filenames.xml:100 +#: ../en/ch06-filenames.xml:100 msgid "Using patterns to identify files" msgstr "使用模式标识文件" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:102 +#: ../en/ch06-filenames.xml:102 msgid "" "In addition to working with file and directory names, Mercurial lets you use " "<emphasis>patterns</emphasis> to identify files. Mercurial's pattern " @@ -7732,7 +6815,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:106 +#: ../en/ch06-filenames.xml:106 msgid "" "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 " @@ -7742,21 +6825,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:113 +#: ../en/ch06-filenames.xml:113 msgid "" "To provide a pattern in place of a regular name on the command line, the " "mechanism is simple:" msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:116 +#: ../en/ch06-filenames.xml:116 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:120 +#: ../en/ch06-filenames.xml:120 msgid "" "Mercurial supports two kinds of pattern syntax. The most frequently used is " "called <literal>glob</literal>; this is the same kind of pattern matching " @@ -7765,7 +6848,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:125 +#: ../en/ch06-filenames.xml:125 msgid "" "When Mercurial does automatic pattern matching on Windows, it uses " "<literal>glob</literal> syntax. You can thus omit the <quote><literal>glob:</" @@ -7773,14 +6856,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:130 +#: ../en/ch06-filenames.xml:130 msgid "" "The <literal>re</literal> syntax is more powerful; it lets you specify " "patterns using regular expressions, also known as regexps." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:134 +#: ../en/ch06-filenames.xml:134 msgid "" "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 " @@ -7788,12 +6871,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch07-filenames.xml:140 +#: ../en/ch06-filenames.xml:140 msgid "Shell-style <literal>glob</literal> patterns" msgstr "外壳风格的 <literal>glob</literal> 模式" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:142 +#: ../en/ch06-filenames.xml:142 msgid "" "This is an overview of the kinds of patterns you can use when you're matching " "on glob patterns." @@ -7801,7 +6884,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:145 +#: ../en/ch06-filenames.xml:145 msgid "" "The <quote><literal>*</literal></quote> character matches any string, within " "a single directory." @@ -7809,7 +6892,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:150 +#: ../en/ch06-filenames.xml:150 msgid "" "The <quote><literal>**</literal></quote> pattern matches any string, and " "crosses directory boundaries. It's not a standard Unix glob token, but it's " @@ -7818,13 +6901,13 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:157 +#: ../en/ch06-filenames.xml:157 msgid "" "The <quote><literal>?</literal></quote> pattern matches any single character." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:162 +#: ../en/ch06-filenames.xml:162 msgid "" "The <quote><literal>[</literal></quote> character begins a " "<emphasis>character class</emphasis>. This matches any single character " @@ -7835,7 +6918,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:172 +#: ../en/ch06-filenames.xml:172 msgid "" "If the first character after the <quote><literal>[</literal></quote> in a " "character class is a <quote><literal>!</literal></quote>, it " @@ -7844,7 +6927,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:178 +#: ../en/ch06-filenames.xml:178 msgid "" "A <quote><literal>{</literal></quote> begins a group of subpatterns, where " "the whole group matches if any subpattern in the group matches. The " @@ -7853,13 +6936,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch07-filenames.xml:187 +#: ../en/ch06-filenames.xml:187 msgid "Watch out!" msgstr "千万小心!" # #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch07-filenames.xml:189 +#: ../en/ch06-filenames.xml:189 msgid "" "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 " @@ -7869,12 +6952,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch07-filenames.xml:201 +#: ../en/ch06-filenames.xml:201 msgid "Regular expression matching with <literal>re</literal> patterns" msgstr "使用 <literal>re</literal> 模式的正则表达式匹配" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:204 +#: ../en/ch06-filenames.xml:204 msgid "" "Mercurial accepts the same regular expression syntax as the Python " "programming language (it uses Python's regexp engine internally). This is " @@ -7883,7 +6966,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:210 +#: ../en/ch06-filenames.xml:210 msgid "" "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 " @@ -7893,7 +6976,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:217 +#: ../en/ch06-filenames.xml:217 msgid "" "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 " @@ -7903,7 +6986,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:224 +#: ../en/ch06-filenames.xml:224 msgid "" "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 " @@ -7913,12 +6996,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch07-filenames.xml:234 +#: ../en/ch06-filenames.xml:234 msgid "Filtering files" msgstr "过滤文件" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:236 +#: ../en/ch06-filenames.xml:236 msgid "" "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 " @@ -7926,7 +7009,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch07-filenames.xml:241 +#: ../en/ch06-filenames.xml:241 msgid "" "<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 " @@ -7934,7 +7017,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch07-filenames.xml:246 +#: ../en/ch06-filenames.xml:246 msgid "" "<option role=\"hg-opt-global\">-X</option>, or <option role=\"hg-opt-global" "\">--exclude</option>, gives you a way to <emphasis>avoid</emphasis> " @@ -7942,7 +7025,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:251 +#: ../en/ch06-filenames.xml:251 msgid "" "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 " @@ -7951,36 +7034,36 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:258 +#: ../en/ch06-filenames.xml:258 msgid "" "You can read a <option role=\"hg-opt-global\">-I</option> filter as " "<quote>process only the files that match this filter</quote>." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:264 +#: ../en/ch06-filenames.xml:264 msgid "" "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>." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch07-filenames.xml:272 +#: ../en/ch06-filenames.xml:272 msgid "Ignoring unwanted files and directories" msgstr "忽略不需要的文件和目录" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:274 +#: ../en/ch06-filenames.xml:274 msgid "XXX." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch07-filenames.xml:278 +#: ../en/ch06-filenames.xml:278 msgid "Case sensitivity" msgstr "大小写敏感性" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:280 +#: ../en/ch06-filenames.xml:280 msgid "" "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 " @@ -7991,7 +7074,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:289 +#: ../en/ch06-filenames.xml:289 msgid "" "Operating systems and filesystems differ in the way they handle the " "<emphasis>case</emphasis> of characters in file and directory names. There " @@ -7999,7 +7082,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch07-filenames.xml:294 +#: ../en/ch06-filenames.xml:294 msgid "" "Completely case insensitive. Uppercase and lowercase versions of a letter " "are treated as identical, both when creating a file and during subsequent " @@ -8007,7 +7090,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch07-filenames.xml:299 +#: ../en/ch06-filenames.xml:299 msgid "" "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 " @@ -8019,7 +7102,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch07-filenames.xml:310 +#: ../en/ch06-filenames.xml:310 msgid "" "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 " @@ -8027,7 +7110,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch07-filenames.xml:316 +#: ../en/ch06-filenames.xml:316 msgid "" "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 " @@ -8036,12 +7119,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch07-filenames.xml:323 +#: ../en/ch06-filenames.xml:323 msgid "Safe, portable repository storage" msgstr "安全,可移植的版本库存储" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:325 +#: ../en/ch06-filenames.xml:325 msgid "" "Mercurial's repository storage mechanism is <emphasis>case safe</emphasis>. " "It translates file names so that they can be safely stored on both case " @@ -8052,12 +7135,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch07-filenames.xml:336 +#: ../en/ch06-filenames.xml:336 msgid "Detecting case conflicts" msgstr "检测大小写冲突" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:338 +#: ../en/ch06-filenames.xml:338 msgid "" "When operating in the working directory, Mercurial honours the naming policy " "of the filesystem where the working directory is located. If the filesystem " @@ -8066,7 +7149,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:344 +#: ../en/ch06-filenames.xml:344 msgid "" "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 " @@ -8079,7 +7162,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:355 +#: ../en/ch06-filenames.xml:355 msgid "" "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. " @@ -8091,12 +7174,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch07-filenames.xml:367 +#: ../en/ch06-filenames.xml:367 msgid "Fixing a case conflict" msgstr "修正大小写冲突" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:369 +#: ../en/ch06-filenames.xml:369 msgid "" "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 " @@ -8106,7 +7189,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:376 +#: ../en/ch06-filenames.xml:376 msgid "" "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 " @@ -8118,7 +7201,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch07-filenames.xml:386 +#: ../en/ch06-filenames.xml:386 msgid "" "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</" @@ -8127,7 +7210,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch07-filenames.xml:393 +#: ../en/ch06-filenames.xml:393 msgid "" "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 " @@ -8136,12 +7219,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch08-branch.xml:5 +#: ../en/ch07-branch.xml:5 msgid "Managing releases and branchy development" msgstr "发布管理与分支开发" #. type: Content of: <book><chapter><para> -#: ../en/ch08-branch.xml:7 +#: ../en/ch07-branch.xml:7 msgid "" "Mercurial provides several mechanisms for you to manage a project that is " "making progress on multiple fronts at once. To understand these mechanisms, " @@ -8149,7 +7232,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch08-branch.xml:12 +#: ../en/ch07-branch.xml:12 msgid "" "Many software projects issue periodic <quote>major</quote> releases that " "contain substantial new features. In parallel, they may issue <quote>minor</" @@ -8158,7 +7241,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch08-branch.xml:18 +#: ../en/ch07-branch.xml:18 msgid "" "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 " @@ -8167,12 +7250,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:25 +#: ../en/ch07-branch.xml:25 msgid "Giving a persistent name to a revision" msgstr "给版本指定一个永久的名称" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:27 +#: ../en/ch07-branch.xml:27 msgid "" "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 " @@ -8183,7 +7266,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:34 +#: ../en/ch07-branch.xml:34 msgid "" "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 " @@ -8191,7 +7274,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:40 +#: ../en/ch07-branch.xml:40 msgid "" "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 " @@ -8202,22 +7285,22 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch08-branch.xml:49 +#: ../en/ch07-branch.xml:49 msgid "Colon (ASCII 58, <quote><literal>:</literal></quote>)" msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch08-branch.xml:52 +#: ../en/ch07-branch.xml:52 msgid "Carriage return (ASCII 13, <quote><literal>\\r</literal></quote>)" msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch08-branch.xml:55 +#: ../en/ch07-branch.xml:55 msgid "Newline (ASCII 10, <quote><literal>\\n</literal></quote>)" msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:59 +#: ../en/ch07-branch.xml:59 msgid "" "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 " @@ -8226,7 +7309,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:67 +#: ../en/ch07-branch.xml:67 msgid "" "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 " @@ -8235,7 +7318,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:73 +#: ../en/ch07-branch.xml:73 msgid "" "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 " @@ -8245,7 +7328,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:80 +#: ../en/ch07-branch.xml:80 msgid "" "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." @@ -8253,7 +7336,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:86 +#: ../en/ch07-branch.xml:86 msgid "" "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 " @@ -8261,7 +7344,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:93 +#: ../en/ch07-branch.xml:93 msgid "" "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, " @@ -8272,7 +7355,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:101 +#: ../en/ch07-branch.xml:101 msgid "" "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 " @@ -8283,14 +7366,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:109 +#: ../en/ch07-branch.xml:109 msgid "" "If you want to remove a tag that you no longer want, use <command role=\"hg-" "cmd\">hg tag --remove</command>." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:114 +#: ../en/ch07-branch.xml:114 msgid "" "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> " @@ -8300,7 +7383,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:123 +#: ../en/ch07-branch.xml:123 msgid "" "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 " @@ -8310,7 +7393,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:129 +#: ../en/ch07-branch.xml:129 msgid "" "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 " @@ -8322,12 +7405,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch08-branch.xml:142 +#: ../en/ch07-branch.xml:142 msgid "Handling tag conflicts during a merge" msgstr "在合并期间处理标签冲突" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch08-branch.xml:144 +#: ../en/ch07-branch.xml:144 msgid "" "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. " @@ -8337,7 +7420,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch08-branch.xml:151 +#: ../en/ch07-branch.xml:151 msgid "" "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 " @@ -8348,7 +7431,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch08-branch.xml:161 +#: ../en/ch07-branch.xml:161 msgid "" "An unfortunate consequence of this design is that you can't actually verify " "that your merged <filename role=\"special\">.hgtags</filename> file is " @@ -8362,12 +7445,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch08-branch.xml:176 +#: ../en/ch07-branch.xml:176 msgid "Tags and cloning" msgstr "标签与克隆" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch08-branch.xml:178 +#: ../en/ch07-branch.xml:178 msgid "" "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 " @@ -8377,7 +7460,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch08-branch.xml:186 +#: ../en/ch07-branch.xml:186 msgid "" "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 " @@ -8391,12 +7474,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch08-branch.xml:201 +#: ../en/ch07-branch.xml:201 msgid "When permanent tags are too much" msgstr "当永久标签太多的时候" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch08-branch.xml:203 +#: ../en/ch07-branch.xml:203 msgid "" "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 " @@ -8407,7 +7490,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch08-branch.xml:213 +#: ../en/ch07-branch.xml:213 msgid "" "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" @@ -8421,12 +7504,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:228 +#: ../en/ch07-branch.xml:228 msgid "The flow of changes&emdash;big picture vs. little" msgstr "修改流程&emdash;宏观与微观" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:230 +#: ../en/ch07-branch.xml:230 msgid "" "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 " @@ -8434,7 +7517,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:234 +#: ../en/ch07-branch.xml:234 msgid "" "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</" @@ -8442,7 +7525,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:239 +#: ../en/ch07-branch.xml:239 msgid "" "The usual way people refer to these different concurrent directions of " "development is as <quote>branches</quote>. However, we've already seen " @@ -8452,14 +7535,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch08-branch.xml:246 +#: ../en/ch07-branch.xml:246 msgid "" "<quote>Big picture</quote> branches represent the sweep of a project's " "evolution; people give them names, and talk about them in conversation." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch08-branch.xml:250 +#: ../en/ch07-branch.xml:250 msgid "" "<quote>Little picture</quote> branches are artefacts of the day-to-day " "activity of developing and merging changes. They expose the narrative of how " @@ -8467,12 +7550,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:258 +#: ../en/ch07-branch.xml:258 msgid "Managing big-picture branches in repositories" msgstr "在版本库中管理分支" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:260 +#: ../en/ch07-branch.xml:260 msgid "" "The easiest way to isolate a <quote>big picture</quote> branch in Mercurial " "is in a dedicated repository. If you have an existing shared " @@ -8483,14 +7566,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:270 +#: ../en/ch07-branch.xml:270 msgid "" "You can then clone a new shared <literal>myproject-1.0.1</literal> repository " "as of that tag." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:276 +#: ../en/ch07-branch.xml:276 msgid "" "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</" @@ -8498,19 +7581,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:283 +#: ../en/ch07-branch.xml:283 msgid "" "Meanwhile, development for the next major release can continue, isolated and " "unabated, in the <literal>myproject</literal> repository." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:291 +#: ../en/ch07-branch.xml:291 msgid "Don't repeat yourself: merging across branches" msgstr "不要重复劳动:在分支间合并" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:293 +#: ../en/ch07-branch.xml:293 msgid "" "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 " @@ -8520,26 +7603,26 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:301 +#: ../en/ch07-branch.xml:301 msgid "" "In the simplest instance, all you need to do is pull changes from your " "maintenance branch into your local clone of the target branch." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:307 +#: ../en/ch07-branch.xml:307 msgid "" "You'll then need to merge the heads of the two branches, and push back to the " "main branch." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:314 +#: ../en/ch07-branch.xml:314 msgid "Naming branches within one repository" msgstr "版本库中的命名分支" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:316 +#: ../en/ch07-branch.xml:316 msgid "" "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 " @@ -8549,7 +7632,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:323 +#: ../en/ch07-branch.xml:323 msgid "" "If you're more in the <quote>power user</quote> category (<emphasis>and</" "emphasis> your collaborators are too), there is an alternative way of " @@ -8562,7 +7645,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:334 +#: ../en/ch07-branch.xml:334 msgid "" "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 " @@ -8572,7 +7655,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:341 +#: ../en/ch07-branch.xml:341 msgid "" "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, " @@ -8582,7 +7665,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:348 +#: ../en/ch07-branch.xml:348 msgid "" "To start working with named branches, use the <command role=\"hg-cmd\">hg " "branches</command> command. This command lists the named branches already " @@ -8590,14 +7673,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:355 +#: ../en/ch07-branch.xml:355 msgid "" "Since you haven't created any named branches yet, the only one that exists is " "<literal>default</literal>." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:358 +#: ../en/ch07-branch.xml:358 msgid "" "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 " @@ -8606,7 +7689,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:365 +#: ../en/ch07-branch.xml:365 msgid "" "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 " @@ -8614,7 +7697,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:371 +#: ../en/ch07-branch.xml:371 msgid "" "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=" @@ -8623,7 +7706,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:378 +#: ../en/ch07-branch.xml:378 msgid "" "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</" @@ -8632,7 +7715,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:385 +#: ../en/ch07-branch.xml:385 msgid "" "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> " @@ -8643,7 +7726,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:395 +#: ../en/ch07-branch.xml:395 msgid "" "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> " @@ -8652,7 +7735,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:400 +#: ../en/ch07-branch.xml:400 msgid "" "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 " @@ -8661,19 +7744,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:408 +#: ../en/ch07-branch.xml:408 msgid "" "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.)" msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:414 +#: ../en/ch07-branch.xml:414 msgid "Dealing with multiple named branches in a repository" msgstr "在版本库中处理多个命名分支" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:417 +#: ../en/ch07-branch.xml:417 msgid "" "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 " @@ -8686,7 +7769,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:427 +#: ../en/ch07-branch.xml:427 msgid "" "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 " @@ -8694,14 +7777,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:433 +#: ../en/ch07-branch.xml:433 msgid "" "We're on the <literal>bar</literal> branch, but there also exists an older " "<command role=\"hg-cmd\">hg foo</command> branch." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:437 +#: ../en/ch07-branch.xml:437 msgid "" "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 " @@ -8712,7 +7795,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:446 +#: ../en/ch07-branch.xml:446 msgid "" "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>, " @@ -8721,19 +7804,19 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:453 +#: ../en/ch07-branch.xml:453 msgid "" "Committing a new change on the <literal>foo</literal> branch introduces a new " "head." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:460 +#: ../en/ch07-branch.xml:460 msgid "Branch names and merging" msgstr "分支名称与合并" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:462 +#: ../en/ch07-branch.xml:462 msgid "" "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" @@ -8745,7 +7828,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:472 +#: ../en/ch07-branch.xml:472 msgid "" "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 " @@ -8755,7 +7838,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:479 +#: ../en/ch07-branch.xml:479 msgid "" "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> " @@ -8767,7 +7850,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:487 +#: ../en/ch07-branch.xml:487 msgid "" "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 " @@ -8775,7 +7858,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:493 +#: ../en/ch07-branch.xml:493 msgid "" "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 " @@ -8785,12 +7868,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch08-branch.xml:502 +#: ../en/ch07-branch.xml:502 msgid "Branch naming is generally useful" msgstr "分支名称通常都很有用" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:504 +#: ../en/ch07-branch.xml:504 msgid "" "You shouldn't think of named branches as applicable only to situations where " "you have multiple long-lived branches cohabiting in a single repository. " @@ -8798,7 +7881,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:509 +#: ../en/ch07-branch.xml:509 msgid "" "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 " @@ -8807,7 +7890,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch08-branch.xml:514 +#: ../en/ch07-branch.xml:514 msgid "" "If you're working with shared repositories, you can set up a <literal role=" "\"hook\">pretxnchangegroup</literal> hook on each that will block incoming " @@ -8819,12 +7902,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch09-undo.xml:5 +#: ../en/ch08-undo.xml:5 msgid "Finding and fixing mistakes" msgstr "查找和修改错误" #. type: Content of: <book><chapter><para> -#: ../en/ch09-undo.xml:7 +#: ../en/ch08-undo.xml:7 msgid "" "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 " @@ -8834,17 +7917,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch09-undo.xml:15 +#: ../en/ch08-undo.xml:15 msgid "Erasing local history" msgstr "销毁本地历史" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:18 +#: ../en/ch08-undo.xml:18 msgid "The accidental commit" msgstr "意外的提交" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:20 +#: ../en/ch08-undo.xml:20 msgid "" "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 " @@ -8855,12 +7938,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:31 +#: ../en/ch08-undo.xml:31 msgid "Rolling back a transaction" msgstr "回滚一个事务" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:33 +#: ../en/ch08-undo.xml:33 msgid "" "In section <xref linkend=\"sec.concepts.txn\"/>, I mentioned that Mercurial " "treats each modification of a repository as a <emphasis>transaction</" @@ -8873,7 +7956,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:43 +#: ../en/ch08-undo.xml:43 msgid "" "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 " @@ -8881,14 +7964,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:50 +#: ../en/ch08-undo.xml:50 msgid "" "Looking at the output of <command role=\"hg-cmd\">hg status</command> after " "the commit immediately confirms the error." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:56 +#: ../en/ch08-undo.xml:56 msgid "" "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 " @@ -8899,7 +7982,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:65 +#: ../en/ch08-undo.xml:65 msgid "" "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, " @@ -8907,7 +7990,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:72 +#: ../en/ch08-undo.xml:72 msgid "" "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</" @@ -8918,12 +8001,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:85 +#: ../en/ch08-undo.xml:85 msgid "The erroneous pull" msgstr "错误的抓取" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:87 +#: ../en/ch08-undo.xml:87 msgid "" "It's common practice with Mercurial to maintain separate development branches " "of a project in different repositories. Your development team might have one " @@ -8932,7 +8015,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:94 +#: ../en/ch08-undo.xml:94 msgid "" "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 " @@ -8945,7 +8028,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:106 +#: ../en/ch08-undo.xml:106 msgid "" "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 " @@ -8955,12 +8038,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:115 +#: ../en/ch08-undo.xml:115 msgid "Rolling back is useless once you've pushed" msgstr "当完成推送后,回滚是无效的" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:117 +#: ../en/ch08-undo.xml:117 msgid "" "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 " @@ -8971,7 +8054,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:126 +#: ../en/ch08-undo.xml:126 msgid "" "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,</" @@ -8982,7 +8065,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:135 +#: ../en/ch08-undo.xml:135 msgid "" "(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</" @@ -8994,12 +8077,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:147 +#: ../en/ch08-undo.xml:147 msgid "You can only roll back once" msgstr "你只能回滚一次" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:149 +#: ../en/ch08-undo.xml:149 msgid "" "Mercurial stores exactly one transaction in its transaction log; that " "transaction is the most recent one that occurred in the repository. This " @@ -9009,19 +8092,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:158 +#: ../en/ch08-undo.xml:158 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch09-undo.xml:165 +#: ../en/ch08-undo.xml:165 msgid "Reverting the mistaken change" msgstr "撤销错误的修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:167 +#: ../en/ch08-undo.xml:167 msgid "" "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 " @@ -9033,7 +8116,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:176 +#: ../en/ch08-undo.xml:176 msgid "" "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 " @@ -9041,14 +8124,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:183 +#: ../en/ch08-undo.xml:183 msgid "" "If we don't want that change, we can simply <command role=\"hg-cmd\">hg " "revert</command> the file." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:189 +#: ../en/ch08-undo.xml:189 msgid "" "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</" @@ -9056,7 +8139,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:196 +#: ../en/ch08-undo.xml:196 msgid "" "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 " @@ -9064,26 +8147,26 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:201 +#: ../en/ch08-undo.xml:201 msgid "If you modify a file, it will restore the file to its unmodified state." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:204 +#: ../en/ch08-undo.xml:204 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:208 +#: ../en/ch08-undo.xml:208 msgid "" "If you delete a file without telling Mercurial, it will restore the file to " "its unmodified contents." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:211 +#: ../en/ch08-undo.xml:211 msgid "" "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 " @@ -9091,12 +8174,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:218 +#: ../en/ch08-undo.xml:218 msgid "File management errors" msgstr "文件管理错误" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:220 +#: ../en/ch08-undo.xml:220 msgid "" "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 " @@ -9106,7 +8189,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:226 +#: ../en/ch08-undo.xml:226 msgid "" "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 " @@ -9115,7 +8198,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:234 +#: ../en/ch08-undo.xml:234 msgid "" "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 " @@ -9127,7 +8210,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:245 +#: ../en/ch08-undo.xml:245 msgid "" "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 " @@ -9136,12 +8219,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch09-undo.xml:254 +#: ../en/ch08-undo.xml:254 msgid "A slightly special case: reverting a rename" msgstr "一个稍微特别的案例:撤销改名" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch09-undo.xml:256 +#: ../en/ch08-undo.xml:256 msgid "" "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 " @@ -9150,7 +8233,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch09-undo.xml:264 +#: ../en/ch08-undo.xml:264 msgid "" "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 " @@ -9159,19 +8242,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch09-undo.xml:273 +#: ../en/ch08-undo.xml:273 msgid "" "So remember, to revert a <command role=\"hg-cmd\">hg rename</command>, you " "must provide <emphasis>both</emphasis> the source and destination names." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch09-undo.xml:278 +#: ../en/ch08-undo.xml:278 msgid "% TODO: the output doesn't look like it will be removed!" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch09-undo.xml:281 +#: ../en/ch08-undo.xml:281 msgid "" "(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 " @@ -9181,19 +8264,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch09-undo.xml:288 +#: ../en/ch08-undo.xml:288 msgid "" "These fiddly aspects of reverting a rename arguably constitute a small bug in " "Mercurial." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch09-undo.xml:295 +#: ../en/ch08-undo.xml:295 msgid "Dealing with committed changes" msgstr "处理已经提交的修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:297 +#: ../en/ch08-undo.xml:297 msgid "" "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 " @@ -9202,7 +8285,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:303 +#: ../en/ch08-undo.xml:303 msgid "" "Before you read this section, here's something to keep in mind: the <command " "role=\"hg-cmd\">hg backout</command> command undoes changes by " @@ -9213,12 +8296,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:312 +#: ../en/ch08-undo.xml:312 msgid "Backing out a changeset" msgstr "恢复一个修改集" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:314 +#: ../en/ch08-undo.xml:314 msgid "" "The <command role=\"hg-cmd\">hg backout</command> command lets you " "<quote>undo</quote> the effects of an entire changeset in an automated " @@ -9230,7 +8313,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:323 +#: ../en/ch08-undo.xml:323 msgid "" "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 " @@ -9238,7 +8321,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:330 +#: ../en/ch08-undo.xml:330 msgid "" "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, " @@ -9249,17 +8332,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:341 +#: ../en/ch08-undo.xml:341 msgid "Backing out the tip changeset" msgstr "恢复顶点修改集" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:343 +#: ../en/ch08-undo.xml:343 msgid "We're going to start by backing out the last changeset we committed." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:348 +#: ../en/ch08-undo.xml:348 msgid "" "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 " @@ -9273,25 +8356,25 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch09-undo.xml:364 +#: ../en/ch08-undo.xml:364 msgid "" "<imageobject><imagedata fileref=\"images/undo-simple.png\"/> </imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch09-undo.xml:367 ../en/ch09-undo.xml:486 +#: ../en/ch08-undo.xml:367 ../en/ch08-undo.xml:486 msgid "" "Backing out a change using the <command role=\"hg-cmd\">hg backout</command> " "command" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:376 +#: ../en/ch08-undo.xml:376 msgid "Backing out a non-tip change" msgstr "恢复非顶点的修改" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:378 +#: ../en/ch08-undo.xml:378 msgid "" "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 " @@ -9299,14 +8382,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:385 +#: ../en/ch08-undo.xml:385 msgid "" "This makes backing out any changeset a <quote>one-shot</quote> operation " "that's usually simple and fast." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:391 +#: ../en/ch08-undo.xml:391 msgid "" "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, " @@ -9314,7 +8397,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:398 +#: ../en/ch08-undo.xml:398 msgid "" "As the graphical history in figure <xref endterm=\"fig.undo.backout-non-tip." "caption\" linkend=\"fig.undo.backout-non-tip\"/> illustrates, Mercurial " @@ -9327,27 +8410,27 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:410 +#: ../en/ch08-undo.xml:410 msgid "" "% TODO: to me it looks like mercurial doesn't commit the second merge " "automatically!" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch09-undo.xml:415 +#: ../en/ch08-undo.xml:415 msgid "" "<imageobject><imagedata fileref=\"images/undo-non-tip.png\"/> </imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch09-undo.xml:418 +#: ../en/ch08-undo.xml:418 msgid "" "Automated backout of a non-tip change using the <command role=\"hg-cmd\">hg " "backout</command> command" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:424 +#: ../en/ch08-undo.xml:424 msgid "" "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 " @@ -9355,12 +8438,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch09-undo.xml:429 +#: ../en/ch08-undo.xml:429 msgid "Always use the <option role=\"hg-opt-backout\">--merge</option> option" msgstr "始终使用选项 <option role=\"hg-opt-backout\">--merge</option>" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch09-undo.xml:432 +#: ../en/ch08-undo.xml:432 msgid "" "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 " @@ -9370,12 +8453,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:443 +#: ../en/ch08-undo.xml:443 msgid "Gaining more control of the backout process" msgstr "在恢复处理中获得更多控制" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:445 +#: ../en/ch08-undo.xml:445 msgid "" "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-" @@ -9388,14 +8471,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:458 +#: ../en/ch08-undo.xml:458 msgid "" "As with our earlier example, We'll commit a third changeset, then back out " "its parent, and see what happens." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:464 +#: ../en/ch08-undo.xml:464 msgid "" "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 " @@ -9404,7 +8487,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:472 +#: ../en/ch08-undo.xml:472 msgid "" "Again, it's easier to see what has happened by looking at a graph of the " "revision history, in figure <xref endterm=\"fig.undo.backout-manual.caption\" " @@ -9415,13 +8498,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch09-undo.xml:483 +#: ../en/ch08-undo.xml:483 msgid "" "<imageobject><imagedata fileref=\"images/undo-manual.png\"/> </imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:492 +#: ../en/ch08-undo.xml:492 msgid "" "After the <command role=\"hg-cmd\">hg backout</command> command has " "completed, it leaves the new <quote>backout</quote> changeset as the parent " @@ -9429,12 +8512,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:499 +#: ../en/ch08-undo.xml:499 msgid "Now we have two isolated sets of changes." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:503 +#: ../en/ch08-undo.xml:503 msgid "" "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 " @@ -9445,14 +8528,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:513 +#: ../en/ch08-undo.xml:513 msgid "" "To get the third change back into the file, we just do a normal merge of our " "two heads." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:518 +#: ../en/ch08-undo.xml:518 msgid "" "Afterwards, the graphical history of our repository looks like figure <xref " "endterm=\"fig.undo.backout-manual-merge.caption\" linkend=\"fig.undo.backout-" @@ -9460,45 +8543,45 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch09-undo.xml:525 +#: ../en/ch08-undo.xml:525 msgid "" "<imageobject><imagedata fileref=\"images/undo-manual-merge.png\"/> </" "imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch09-undo.xml:528 +#: ../en/ch08-undo.xml:528 msgid "Manually merging a backout change" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:535 +#: ../en/ch08-undo.xml:535 msgid "Why <command role=\"hg-cmd\">hg backout</command> works as it does" msgstr "<command role=\"hg-cmd\">hg backout</command> 的内幕" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:538 +#: ../en/ch08-undo.xml:538 msgid "" "Here's a brief description of how the <command role=\"hg-cmd\">hg backout</" "command> command works." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:541 +#: ../en/ch08-undo.xml:541 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:545 +#: ../en/ch08-undo.xml:545 msgid "" "It remembers the current parent of the working directory. Let's call this " "changeset <literal>orig</literal>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:549 +#: ../en/ch08-undo.xml:549 msgid "" "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 " @@ -9506,14 +8589,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:554 +#: ../en/ch08-undo.xml:554 msgid "" "It finds the parent of that changeset. Let's call that changeset " "<literal>parent</literal>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:557 +#: ../en/ch08-undo.xml:557 msgid "" "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 " @@ -9522,14 +8605,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:564 +#: ../en/ch08-undo.xml:564 msgid "" "It commits the result as a new changeset. This changeset has " "<literal>backout</literal> as its parent." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:568 +#: ../en/ch08-undo.xml:568 msgid "" "If you specify <option role=\"hg-opt-backout\">--merge</option> on the " "command line, it merges with <literal>orig</literal>, and commits the result " @@ -9537,7 +8620,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:574 +#: ../en/ch08-undo.xml:574 msgid "" "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 " @@ -9548,7 +8631,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:584 +#: ../en/ch08-undo.xml:584 msgid "" "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 " @@ -9557,7 +8640,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:591 +#: ../en/ch08-undo.xml:591 msgid "" "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 " @@ -9572,12 +8655,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch09-undo.xml:608 +#: ../en/ch08-undo.xml:608 msgid "Changes that should never have been" msgstr "不该发生的修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:610 +#: ../en/ch08-undo.xml:610 msgid "" "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 " @@ -9586,7 +8669,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:616 +#: ../en/ch08-undo.xml:616 msgid "" "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 " @@ -9598,7 +8681,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:625 +#: ../en/ch08-undo.xml:625 msgid "" "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 " @@ -9607,7 +8690,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:630 +#: ../en/ch08-undo.xml:630 msgid "" "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 " @@ -9619,7 +8702,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:639 +#: ../en/ch08-undo.xml:639 msgid "" "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 " @@ -9629,7 +8712,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:647 +#: ../en/ch08-undo.xml:647 msgid "" "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 " @@ -9640,7 +8723,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:655 +#: ../en/ch08-undo.xml:655 msgid "" "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 " @@ -9649,7 +8732,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:661 +#: ../en/ch08-undo.xml:661 msgid "" "XXX This needs filling out. The <literal>hg-replay</literal> script in the " "<literal>examples</literal> directory works, but doesn't handle merge " @@ -9657,12 +8740,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:667 +#: ../en/ch08-undo.xml:667 msgid "Protect yourself from <quote>escaped</quote> changes" msgstr "使用<quote>校验</quote>修改来保护你自己" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:670 +#: ../en/ch08-undo.xml:670 msgid "" "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 " @@ -9672,7 +8755,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:677 +#: ../en/ch08-undo.xml:677 msgid "" "By configuring some hooks on that repository to validate incoming changesets " "(see chapter <xref linkend=\"chap.hook\"/>), you can automatically prevent " @@ -9684,7 +8767,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:687 +#: ../en/ch08-undo.xml:687 msgid "" "For instance, an incoming change hook that verifies that a changeset will " "actually compile can prevent people from inadvertantly <quote>breaking the " @@ -9692,12 +8775,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch09-undo.xml:694 +#: ../en/ch08-undo.xml:694 msgid "Finding the source of a bug" msgstr "查找问题的根源" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:696 +#: ../en/ch08-undo.xml:696 msgid "" "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 " @@ -9707,7 +8790,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:703 +#: ../en/ch08-undo.xml:703 msgid "" "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 " @@ -9719,14 +8802,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:712 +#: ../en/ch08-undo.xml:712 msgid "" "Here are a few scenarios to help you understand how you might apply this " "command." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:715 +#: ../en/ch08-undo.xml:715 msgid "" "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, " @@ -9734,7 +8817,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:720 +#: ../en/ch08-undo.xml:720 msgid "" "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 " @@ -9743,7 +8826,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:727 +#: ../en/ch08-undo.xml:727 msgid "" "Your software works correctly, but runs 15% slower than the last time you " "measured it. You want to know which changeset introduced the performance " @@ -9752,14 +8835,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:734 +#: ../en/ch08-undo.xml:734 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:739 +#: ../en/ch08-undo.xml:739 msgid "" "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. " @@ -9769,7 +8852,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:746 +#: ../en/ch08-undo.xml:746 msgid "" "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 " @@ -9783,7 +8866,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:759 +#: ../en/ch08-undo.xml:759 msgid "" "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 " @@ -9796,7 +8879,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:769 +#: ../en/ch08-undo.xml:769 msgid "" "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 " @@ -9809,7 +8892,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch09-undo.xml:780 +#: ../en/ch08-undo.xml:780 msgid "" "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 " @@ -9819,18 +8902,18 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:788 +#: ../en/ch08-undo.xml:788 msgid "Using the <command role=\"hg-cmd\">hg bisect</command> command" msgstr "使用命令 <command role=\"hg-cmd\">hg bisect</command>" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:791 +#: ../en/ch08-undo.xml:791 msgid "" "Here's an example of <command role=\"hg-cmd\">hg bisect</command> in action." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch09-undo.xml:795 +#: ../en/ch08-undo.xml:795 msgid "" "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 " @@ -9839,14 +8922,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:802 +#: ../en/ch08-undo.xml:802 msgid "" "Now let's create a repository, so that we can try out the <command role=\"hg-" "cmd\">hg bisect</command> command in isolation." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:808 +#: ../en/ch08-undo.xml:808 msgid "" "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 " @@ -9856,7 +8939,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:818 +#: ../en/ch08-undo.xml:818 msgid "" "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 " @@ -9864,46 +8947,46 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:825 +#: ../en/ch08-undo.xml:825 msgid "" "The <command role=\"hg-cmd\">hg bisect</command> command works in steps. " "Each step proceeds as follows." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:828 +#: ../en/ch08-undo.xml:828 msgid "You run your binary test." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:830 +#: ../en/ch08-undo.xml:830 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:835 +#: ../en/ch08-undo.xml:835 msgid "" "If it failed, run the <command role=\"hg-cmd\">hg bisect bad</command> " "command." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:839 +#: ../en/ch08-undo.xml:839 msgid "" "The command uses your information to decide which changeset to test next." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><orderedlist><listitem><para> -#: ../en/ch09-undo.xml:842 +#: ../en/ch08-undo.xml:842 msgid "" "It updates the working directory to that changeset, and the process begins " "again." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:845 +#: ../en/ch08-undo.xml:845 msgid "" "The process ends when <command role=\"hg-cmd\">hg bisect</command> identifies " "a unique changeset that marks the point where your test transitioned from " @@ -9912,14 +8995,14 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:850 +#: ../en/ch08-undo.xml:850 msgid "" "To start the search, we must run the <command role=\"hg-cmd\">hg bisect --" "reset</command> command." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:855 +#: ../en/ch08-undo.xml:855 msgid "" "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, " @@ -9929,7 +9012,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:863 +#: ../en/ch08-undo.xml:863 msgid "" "Most of the time, the revision to which the working directory is synced " "(usually the tip) already exhibits the problem introduced by the buggy " @@ -9938,7 +9021,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:870 +#: ../en/ch08-undo.xml:870 msgid "" "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> " @@ -9949,26 +9032,26 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:880 +#: ../en/ch08-undo.xml:880 msgid "Notice that this command printed some output." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:882 +#: ../en/ch08-undo.xml:882 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:886 +#: ../en/ch08-undo.xml:886 msgid "" "It updated the working directory to the next changeset to test, and told us " "which changeset it's testing." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:891 +#: ../en/ch08-undo.xml:891 msgid "" "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 " @@ -9977,26 +9060,26 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:897 +#: ../en/ch08-undo.xml:897 msgid "" "This test looks like a perfect candidate for automation, so let's turn it " "into a shell function." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:901 +#: ../en/ch08-undo.xml:901 msgid "" "We can now run an entire test step with a single command, <literal>mytest</" "literal>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:906 +#: ../en/ch08-undo.xml:906 msgid "A few more invocations of our canned test step command, and we're done." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:911 +#: ../en/ch08-undo.xml:911 msgid "" "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 " @@ -10008,13 +9091,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:922 +#: ../en/ch08-undo.xml:922 msgid "Cleaning up after your search" msgstr "搜索后的清理" # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:924 +#: ../en/ch08-undo.xml:924 msgid "" "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 " @@ -10026,17 +9109,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch09-undo.xml:939 +#: ../en/ch08-undo.xml:939 msgid "Tips for finding bugs effectively" msgstr "有效查找问题的技巧" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:942 +#: ../en/ch08-undo.xml:942 msgid "Give consistent input" msgstr "给出一致的输入" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:944 +#: ../en/ch08-undo.xml:944 msgid "" "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 " @@ -10048,12 +9131,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:956 +#: ../en/ch08-undo.xml:956 msgid "Automate as much as possible" msgstr "尽量自动" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:958 +#: ../en/ch08-undo.xml:958 msgid "" "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. " @@ -10063,7 +9146,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:966 +#: ../en/ch08-undo.xml:966 msgid "" "My initial problems with driving the <command role=\"hg-cmd\">hg bisect</" "command> command by hand occurred even with simple searches on small " @@ -10074,24 +9157,24 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:975 +#: ../en/ch08-undo.xml:975 msgid "The key to automated testing is twofold:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:977 +#: ../en/ch08-undo.xml:977 msgid "always test for the same symptom, and" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:979 +#: ../en/ch08-undo.xml:979 msgid "" "always feed consistent input to the <command role=\"hg-cmd\">hg bisect</" "command> command." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:982 +#: ../en/ch08-undo.xml:982 msgid "" "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 " @@ -10102,12 +9185,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:992 +#: ../en/ch08-undo.xml:992 msgid "Check your results" msgstr "检查你的结果" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:994 +#: ../en/ch08-undo.xml:994 msgid "" "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 " @@ -10116,32 +9199,32 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:1000 +#: ../en/ch08-undo.xml:1000 msgid "" "The changeset that it reports as the first bad revision. Your test should " "still report this as bad." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:1004 +#: ../en/ch08-undo.xml:1004 msgid "" "The parent of that changeset (either parent, if it's a merge). Your test " "should report this changeset as good." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch09-undo.xml:1008 +#: ../en/ch08-undo.xml:1008 msgid "" "A child of that changeset. Your test should report this changeset as bad." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:1014 +#: ../en/ch08-undo.xml:1014 msgid "Beware interference between bugs" msgstr "谨防问题之间的冲突" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:1016 +#: ../en/ch08-undo.xml:1016 msgid "" "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, " @@ -10151,7 +9234,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:1024 +#: ../en/ch08-undo.xml:1024 msgid "" "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 " @@ -10163,7 +9246,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:1034 +#: ../en/ch08-undo.xml:1034 msgid "" "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 " @@ -10172,7 +9255,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:1041 +#: ../en/ch08-undo.xml:1041 msgid "" "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 " @@ -10181,12 +9264,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch09-undo.xml:1050 +#: ../en/ch08-undo.xml:1050 msgid "Bracket your search lazily" msgstr "减少你的查找工作" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:1052 +#: ../en/ch08-undo.xml:1052 msgid "" "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 " @@ -10196,7 +9279,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:1060 +#: ../en/ch08-undo.xml:1060 msgid "" "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</" @@ -10207,7 +9290,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch09-undo.xml:1069 +#: ../en/ch08-undo.xml:1069 msgid "" "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 " @@ -10216,12 +9299,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch10-hook.xml:5 +#: ../en/ch09-hook.xml:5 msgid "Handling repository events with hooks" msgstr "使用钩子处理版本库事件" #. type: Content of: <book><chapter><para> -#: ../en/ch10-hook.xml:7 +#: ../en/ch09-hook.xml:7 msgid "" "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 " @@ -10229,7 +9312,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch10-hook.xml:12 +#: ../en/ch09-hook.xml:12 msgid "" "The name Mercurial uses for one of these actions is a <emphasis>hook</" "emphasis>. Hooks are called <quote>triggers</quote> in some revision control " @@ -10237,12 +9320,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:18 +#: ../en/ch09-hook.xml:18 msgid "An overview of hooks in Mercurial" msgstr "Mercurial 钩子概述" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:20 +#: ../en/ch09-hook.xml:20 msgid "" "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." @@ -10250,21 +9333,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:25 +#: ../en/ch09-hook.xml:25 msgid "" "<literal role=\"hook\">changegroup</literal>: This is run after a group of " "changesets has been brought into the repository from elsewhere." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:29 +#: ../en/ch09-hook.xml:29 msgid "" "<literal role=\"hook\">commit</literal>: This is run after a new changeset " "has been created in the local repository." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:33 +#: ../en/ch09-hook.xml:33 msgid "" "<literal role=\"hook\">incoming</literal>: This is run once for each new " "changeset that is brought into the repository from elsewhere. Notice the " @@ -10273,42 +9356,42 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:40 +#: ../en/ch09-hook.xml:40 msgid "" "<literal role=\"hook\">outgoing</literal>: This is run after a group of " "changesets has been transmitted from this repository." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:44 +#: ../en/ch09-hook.xml:44 msgid "" "<literal role=\"hook\">prechangegroup</literal>: This is run before starting " "to bring a group of changesets into the repository." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:49 +#: ../en/ch09-hook.xml:49 msgid "" "<literal role=\"hook\">precommit</literal>: Controlling. This is run before " "starting a commit." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:53 +#: ../en/ch09-hook.xml:53 msgid "" "<literal role=\"hook\">preoutgoing</literal>: Controlling. This is run before " "starting to transmit a group of changesets from this repository." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:58 +#: ../en/ch09-hook.xml:58 msgid "" "<literal role=\"hook\">pretag</literal>: Controlling. This is run before " "creating a tag." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:62 +#: ../en/ch09-hook.xml:62 msgid "" "<literal role=\"hook\">pretxnchangegroup</literal>: Controlling. This is run " "after a group of changesets has been brought into the local repository from " @@ -10317,7 +9400,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:70 +#: ../en/ch09-hook.xml:70 msgid "" "<literal role=\"hook\">pretxncommit</literal>: Controlling. This is run after " "a new changeset has been created in the local repository, but before the " @@ -10325,27 +9408,27 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:76 +#: ../en/ch09-hook.xml:76 msgid "" "<literal role=\"hook\">preupdate</literal>: Controlling. This is run before " "starting an update or merge of the working directory." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:81 +#: ../en/ch09-hook.xml:81 msgid "" "<literal role=\"hook\">tag</literal>: This is run after a tag is created." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:85 +#: ../en/ch09-hook.xml:85 msgid "" "<literal role=\"hook\">update</literal>: This is run after an update or merge " "of the working directory has finished." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:90 +#: ../en/ch09-hook.xml:90 msgid "" "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 " @@ -10354,17 +9437,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:99 +#: ../en/ch09-hook.xml:99 msgid "Hooks and security" msgstr "钩子与安全性" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:102 +#: ../en/ch09-hook.xml:102 msgid "Hooks are run with your privileges" msgstr "钩子以你的特权执行" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:104 +#: ../en/ch09-hook.xml:104 msgid "" "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 " @@ -10375,16 +9458,16 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:115 +#: ../en/ch09-hook.xml:115 msgid "" "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." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:122 +"run hooks defined in that system's global <filename role=\"special\">~/.hgrc</" +"filename> file." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:122 msgid "" "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 " @@ -10396,7 +9479,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch10-hook.xml:134 +#: ../en/ch09-hook.xml:134 msgid "" "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" @@ -10405,7 +9488,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:142 +#: ../en/ch09-hook.xml:142 msgid "" "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 " @@ -10416,12 +9499,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:153 +#: ../en/ch09-hook.xml:153 msgid "Hooks do not propagate" msgstr "钩子不会传播" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:155 +#: ../en/ch09-hook.xml:155 msgid "" "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 " @@ -10430,7 +9513,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:162 +#: ../en/ch09-hook.xml:162 msgid "" "It would be extremely reckless for any distributed revision control system to " "implement revision-controlled hooks, as this would offer an easily " @@ -10439,7 +9522,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:168 +#: ../en/ch09-hook.xml:168 msgid "" "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 " @@ -10448,22 +9531,22 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:175 +#: ../en/ch09-hook.xml:175 msgid "" "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 " +"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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:185 +#: ../en/ch09-hook.xml:184 msgid "Hooks can be overridden" msgstr "钩子可以被覆盖" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:187 +#: ../en/ch09-hook.xml:186 msgid "" "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 " @@ -10471,31 +9554,31 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:192 -msgid "" -"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." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:200 +#: ../en/ch09-hook.xml:191 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:199 msgid "Ensuring that critical hooks are run" msgstr "确保关键钩子的执行" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:202 +#: ../en/ch09-hook.xml:201 msgid "" "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." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:211 +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><para> +#: ../en/ch09-hook.xml:210 msgid "" "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> " @@ -10503,7 +9586,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:217 +#: ../en/ch09-hook.xml:216 msgid "" "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 " @@ -10513,7 +9596,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:226 +#: ../en/ch09-hook.xml:225 msgid "" "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 " @@ -10523,12 +9606,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:237 +#: ../en/ch09-hook.xml:236 msgid "Care with <literal>pretxn</literal> hooks in a shared-access repository" msgstr "在共享版本库中注意 <literal>pretxn</literal> 钩子" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:240 +#: ../en/ch09-hook.xml:239 msgid "" "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 " @@ -10536,7 +9619,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:245 +#: ../en/ch09-hook.xml:244 msgid "" "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 " @@ -10545,7 +9628,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:252 +#: ../en/ch09-hook.xml:251 msgid "" "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 " @@ -10555,7 +9638,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:260 +#: ../en/ch09-hook.xml:259 msgid "" "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 " @@ -10564,7 +9647,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:267 +#: ../en/ch09-hook.xml:266 msgid "" "When Mercurial <emphasis>writes</emphasis> metadata, it writes it straight " "into the destination file. It writes file data first, then manifest data " @@ -10576,7 +9659,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:278 +#: ../en/ch09-hook.xml:277 msgid "" "When Mercurial <emphasis>reads</emphasis> metadata, it reads the changelog " "first, then everything else. Since a reader will only access parts of the " @@ -10585,7 +9668,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:284 +#: ../en/ch09-hook.xml:283 msgid "" "Some controlling hooks (<literal role=\"hook\">pretxncommit</literal> and " "<literal role=\"hook\">pretxnchangegroup</literal>) run when a transaction is " @@ -10594,7 +9677,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:292 +#: ../en/ch09-hook.xml:291 msgid "" "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 " @@ -10603,12 +9686,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:300 +#: ../en/ch09-hook.xml:299 msgid "The problem illustrated" msgstr "问题的演示" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:302 +#: ../en/ch09-hook.xml:301 msgid "" "In principle, a good use for the <literal role=\"hook\">pretxnchangegroup</" "literal> hook would be to automatically build and test incoming changes " @@ -10620,7 +9703,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:313 +#: ../en/ch09-hook.xml:312 msgid "" "The safest technological answer to this challenge is to set up such a " "<quote>gatekeeper</quote> repository as <emphasis>unidirectional</emphasis>. " @@ -10633,7 +9716,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:325 +#: ../en/ch09-hook.xml:324 msgid "" "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. " @@ -10645,7 +9728,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:336 +#: ../en/ch09-hook.xml:335 msgid "" "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</" @@ -10655,12 +9738,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:347 +#: ../en/ch09-hook.xml:346 msgid "A short tutorial on using hooks" msgstr "使用钩子的简短指南" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:349 +#: ../en/ch09-hook.xml:348 msgid "" "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 " @@ -10669,15 +9752,15 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:356 +#: ../en/ch09-hook.xml:355 msgid "All hooks follow the pattern in this example." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:360 +#: ../en/ch09-hook.xml:359 msgid "" "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 " +"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 " @@ -10685,19 +9768,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:370 +#: ../en/ch09-hook.xml:369 msgid "Performing multiple actions per event" msgstr "每个事件执行多个操作" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:372 +#: ../en/ch09-hook.xml:371 msgid "" "Quite often, you will want to define more than one hook for a particular kind " "of event, as shown below." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:377 +#: ../en/ch09-hook.xml:376 msgid "" "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 " @@ -10708,7 +9791,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:388 +#: ../en/ch09-hook.xml:387 msgid "" "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 " @@ -10718,7 +9801,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:397 +#: ../en/ch09-hook.xml:396 msgid "" "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 " @@ -10729,12 +9812,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:408 +#: ../en/ch09-hook.xml:407 msgid "Controlling whether an activity can proceed" msgstr "控制处理的活动" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:410 +#: ../en/ch09-hook.xml:409 msgid "" "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 " @@ -10743,7 +9826,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:417 +#: ../en/ch09-hook.xml:416 msgid "" "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 " @@ -10752,7 +9835,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:423 +#: ../en/ch09-hook.xml:422 msgid "" "The <literal role=\"hook\">pretxncommit</literal> hook runs after a commit " "has all but completed. In other words, the metadata representing the " @@ -10763,7 +9846,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:432 +#: ../en/ch09-hook.xml:431 msgid "" "If the <literal role=\"hook\">pretxncommit</literal> hook exits with a status " "code of zero, the transaction is allowed to complete; the commit finishes; " @@ -10774,19 +9857,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:444 +#: ../en/ch09-hook.xml:443 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:452 +#: ../en/ch09-hook.xml:451 msgid "Writing your own hooks" msgstr "编写钩子" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:454 +#: ../en/ch09-hook.xml:453 msgid "" "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 " @@ -10795,12 +9878,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:463 +#: ../en/ch09-hook.xml:462 msgid "Choosing how your hook should run" msgstr "选择钩子的执行方式" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:465 +#: ../en/ch09-hook.xml:464 msgid "" "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 " @@ -10808,7 +9891,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:470 +#: ../en/ch09-hook.xml:469 msgid "" "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 " @@ -10817,7 +9900,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:477 +#: ../en/ch09-hook.xml:476 msgid "" "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 " @@ -10827,7 +9910,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:485 +#: ../en/ch09-hook.xml:484 msgid "" "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 " @@ -10836,12 +9919,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:494 +#: ../en/ch09-hook.xml:493 msgid "Hook parameters" msgstr "钩子的参数" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:496 +#: ../en/ch09-hook.xml:495 msgid "" "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 " @@ -10849,7 +9932,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:502 +#: ../en/ch09-hook.xml:501 msgid "" "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 " @@ -10862,12 +9945,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:516 +#: ../en/ch09-hook.xml:515 msgid "Hook return values and activity control" msgstr "钩子的返回值与活动控制" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:518 +#: ../en/ch09-hook.xml:517 msgid "" "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 " @@ -10877,35 +9960,35 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:526 +#: ../en/ch09-hook.xml:525 msgid "" "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>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:533 +#: ../en/ch09-hook.xml:532 msgid "Writing an external hook" msgstr "编写外部钩子" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:535 -msgid "" -"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 " +#: ../en/ch09-hook.xml:534 +msgid "" +"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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:542 +#: ../en/ch09-hook.xml:541 msgid "" "An executable hook is always run with its current directory set to a " "repository's root directory." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:546 +#: ../en/ch09-hook.xml:545 msgid "" "Each hook parameter is passed in as an environment variable; the name is " "upper-cased, and prefixed with the string <quote><literal>HG_</literal></" @@ -10913,7 +9996,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:551 +#: ../en/ch09-hook.xml:550 msgid "" "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 " @@ -10924,14 +10007,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:562 +#: ../en/ch09-hook.xml:561 msgid "Telling Mercurial to use an in-process hook" msgstr "让 Mercurial 使用进程内钩子" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:564 -msgid "" -"The <filename role=\"special\"> /.hgrc</filename>\\ syntax for defining an in-" +#: ../en/ch09-hook.xml:563 +msgid "" +"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 " @@ -10939,7 +10022,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:572 +#: ../en/ch09-hook.xml:571 msgid "" "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> " @@ -10947,14 +10030,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:578 -msgid "" -"The following <filename role=\"special\"> /.hgrc</filename>\\ example snippet " +#: ../en/ch09-hook.xml:577 +msgid "" +"The following <filename role=\"special\">~/.hgrc</filename> example snippet " "illustrates the syntax and meaning of the notions we just described." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:584 +#: ../en/ch09-hook.xml:583 msgid "" "When Mercurial runs the <literal>commit.example</literal> hook, it imports " "<literal>mymodule.submodule</literal>, looks for the callable object named " @@ -10962,19 +10045,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:592 +#: ../en/ch09-hook.xml:591 msgid "Writing an in-process hook" msgstr "编写进程内钩子" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:594 +#: ../en/ch09-hook.xml:593 msgid "" "The simplest in-process hook does nothing, but illustrates the basic shape of " "the hook API:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:599 +#: ../en/ch09-hook.xml:598 msgid "" "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 " @@ -10986,17 +10069,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:614 +#: ../en/ch09-hook.xml:613 msgid "Some hook examples" msgstr "钩子样例" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:617 +#: ../en/ch09-hook.xml:616 msgid "Writing meaningful commit messages" msgstr "编写有意义的提交日志" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:619 +#: ../en/ch09-hook.xml:618 msgid "" "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 " @@ -11005,12 +10088,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:629 +#: ../en/ch09-hook.xml:628 msgid "Checking for trailing whitespace" msgstr "检查行尾空格" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:631 +#: ../en/ch09-hook.xml:630 msgid "" "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 " @@ -11022,7 +10105,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:642 +#: ../en/ch09-hook.xml:641 msgid "" "You can use either the <literal role=\"hook\">precommit</literal> or <literal " "role=\"hook\">pretxncommit</literal> hook to tell whether you have a trailing " @@ -11037,7 +10120,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:658 +#: ../en/ch09-hook.xml:657 msgid "" "Should you choose the <literal role=\"hook\">pretxncommit</literal> hook, the " "check won't occur until just before the transaction for the commit " @@ -11049,7 +10132,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:671 +#: ../en/ch09-hook.xml:670 msgid "" "In this example, we introduce a simple <literal role=\"hook\">pretxncommit</" "literal> hook that checks for trailing whitespace. This hook is short, but " @@ -11061,7 +10144,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:682 +#: ../en/ch09-hook.xml:681 msgid "" "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 " @@ -11074,7 +10157,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:696 +#: ../en/ch09-hook.xml:695 msgid "" "As a final aside, note in the example above the use of <command>perl</" "command>'s in-place editing feature to get rid of trailing whitespace from a " @@ -11082,12 +10165,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:706 +#: ../en/ch09-hook.xml:705 msgid "Bundled hooks" msgstr "内置的钩子" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch10-hook.xml:708 +#: ../en/ch09-hook.xml:707 msgid "" "Mercurial ships with several bundled hooks. You can find them in the " "<filename class=\"directory\">hgext</filename> directory of a Mercurial " @@ -11097,14 +10180,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:717 +#: ../en/ch09-hook.xml:716 msgid "" "<literal role=\"hg-ext\">acl</literal>&emdash;access control for parts of a " "repository" msgstr "<literal role=\"hg-ext\">acl</literal>&emdash;版本库的访问控制" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:720 +#: ../en/ch09-hook.xml:719 msgid "" "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 " @@ -11114,7 +10197,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:728 +#: ../en/ch09-hook.xml:727 msgid "" "This extension implements access control based on the identity of the user " "performing a push, <emphasis>not</emphasis> on who committed the changesets " @@ -11124,12 +10207,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:738 +#: ../en/ch09-hook.xml:737 msgid "Configuring the <literal role=\"hook\">acl</literal> hook" msgstr "配置 <literal role=\"hook\">acl</literal> 钩子" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:741 +#: ../en/ch09-hook.xml:740 msgid "" "In order to manage incoming changesets, the <literal role=\"hg-ext\">acl</" "literal> hook must be used as a <literal role=\"hook\">pretxnchangegroup</" @@ -11139,14 +10222,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:751 +#: ../en/ch09-hook.xml:750 msgid "" "The <literal role=\"hg-ext\">acl</literal> extension is configured using " "three sections." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:755 +#: ../en/ch09-hook.xml:754 msgid "" "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 " @@ -11155,7 +10238,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:762 +#: ../en/ch09-hook.xml:761 msgid "" "<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 " @@ -11164,28 +10247,28 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:770 +#: ../en/ch09-hook.xml:769 msgid "" "<envar role=\"rc-item-acl\">pull</envar>: Control incoming changesets that " "are arriving via a pull from a local repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:775 +#: ../en/ch09-hook.xml:774 msgid "" "<envar role=\"rc-item-acl\">push</envar>: Control incoming changesets that " "are arriving via a push from a local repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:780 +#: ../en/ch09-hook.xml:779 msgid "" "<envar role=\"rc-item-acl\">bundle</envar>: Control incoming changesets that " "are arriving from another repository via a bundle." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:786 +#: ../en/ch09-hook.xml:785 msgid "" "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 " @@ -11195,7 +10278,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:795 +#: ../en/ch09-hook.xml:794 msgid "" "The <literal role=\"rc-acl.deny\">acl.deny</literal> section determines which " "users are denied from adding changesets to the repository. If this section " @@ -11203,7 +10286,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:801 +#: ../en/ch09-hook.xml:800 msgid "" "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 " @@ -11212,7 +10295,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:809 +#: ../en/ch09-hook.xml:808 msgid "" "In the following example, the user <literal>docwriter</literal> can only push " "changes to the <filename class=\"directory\">docs</filename> subtree of the " @@ -11221,23 +10304,23 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:821 ../en/ch10-hook.xml:1095 ../en/ch10-hook.xml:1308 +#: ../en/ch09-hook.xml:822 ../en/ch09-hook.xml:1089 ../en/ch09-hook.xml:1280 msgid "Testing and troubleshooting" msgstr "测试与问题处理" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:823 +#: ../en/ch09-hook.xml:824 msgid "" "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</" +"you can enable debugging output in your <filename role=\"special\">~/.hgrc</" "filename>:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:833 +#: ../en/ch09-hook.xml:835 msgid "" "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 " @@ -11245,13 +10328,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:842 +#: ../en/ch09-hook.xml:844 msgid "" "<literal role=\"hg-ext\">bugzilla</literal>&emdash;integration with Bugzilla" msgstr "<literal role=\"hg-ext\">bugzilla</literal>&emdash;与 Bugzilla 的集成" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:846 +#: ../en/ch09-hook.xml:848 msgid "" "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 " @@ -11260,14 +10343,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:853 +#: ../en/ch09-hook.xml:855 msgid "" "It adds a comment to the bug that looks like this (you can configure the " "contents of the comment&emdash;see below):" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:862 +#: ../en/ch09-hook.xml:864 msgid "" "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 " @@ -11276,14 +10359,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:869 +#: ../en/ch09-hook.xml:871 msgid "" "You can use the code in this hook as a starting point for some more exotic " "Bugzilla integration recipes. Here are a few possibilities:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:874 +#: ../en/ch09-hook.xml:876 msgid "" "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 " @@ -11292,7 +10375,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:882 +#: ../en/ch09-hook.xml:884 msgid "" "Allow incoming changesets to automatically modify the <emphasis>state</" "emphasis> of a bug, as well as simply adding a comment. For example, the " @@ -11302,20 +10385,23 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:892 +#: ../en/ch09-hook.xml:894 msgid "Configuring the <literal role=\"hook\">bugzilla</literal> hook" msgstr "配置 <literal role=\"hook\">bugzilla</literal> 钩子" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:895 -msgid "" -"You should configure this hook in your server's <filename role=\"special\"> /." -"hgrc</filename>\\ as an <literal role=\"hook\">incoming</literal> hook, for " +#: ../en/ch09-hook.xml:897 +#, fuzzy +msgid "" +"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:" msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:903 +"选择正确的 <filename role=\"special\"> /.hgrc</filename> 文件增加到 <literal " +"role=\"rc-web\">web</literal> 条目" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:905 msgid "" "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 " @@ -11323,7 +10409,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:909 +#: ../en/ch09-hook.xml:911 msgid "" "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 " @@ -11332,15 +10418,15 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:916 +#: ../en/ch09-hook.xml:918 msgid "" "Configuration information for this hook lives in the <literal role=\"rc-" -"bugzilla\">bugzilla</literal> section of your <filename role=\"special\"> /." +"bugzilla\">bugzilla</literal> section of your <filename role=\"special\">~/." "hgrc</filename>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:921 +#: ../en/ch09-hook.xml:923 msgid "" "<envar role=\"rc-item-bugzilla\">version</envar>: The version of Bugzilla " "installed on the server. The database schema that Bugzilla uses changes " @@ -11349,7 +10435,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:930 +#: ../en/ch09-hook.xml:932 msgid "" "<envar role=\"rc-item-bugzilla\">host</envar>: The hostname of the MySQL " "server that stores your Bugzilla data. The database must be configured to " @@ -11358,7 +10444,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:937 +#: ../en/ch09-hook.xml:939 msgid "" "<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 " @@ -11369,16 +10455,16 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:948 +#: ../en/ch09-hook.xml:950 msgid "" "<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." +"sure that unauthorised users cannot read the <filename role=\"special\">~/." +"hgrc</filename> file where you store this information." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:957 +#: ../en/ch09-hook.xml:959 msgid "" "<envar role=\"rc-item-bugzilla\">db</envar>: The name of the Bugzilla " "database on the MySQL server. The default value of this item is " @@ -11387,7 +10473,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:964 +#: ../en/ch09-hook.xml:966 msgid "" "<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 " @@ -11398,7 +10484,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:977 +#: ../en/ch09-hook.xml:979 msgid "" "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) " @@ -11409,12 +10495,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:992 +#: ../en/ch09-hook.xml:994 msgid "Mapping committer names to Bugzilla user names" msgstr "提交者的名称与 Bugzilla 用户名称的映射" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:994 +#: ../en/ch09-hook.xml:996 msgid "" "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 " @@ -11424,14 +10510,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1003 +#: ../en/ch09-hook.xml:1005 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1010 +#: ../en/ch09-hook.xml:1012 msgid "" "You can either keep the <literal role=\"rc-usermap\">usermap</literal> data " "in a normal <filename role=\"special\">~/.hgrc</filename>, or tell the " @@ -11440,32 +10526,32 @@ "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:" -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1026 +"<filename role=\"special\">~/.hgrc</filename> file might look like this:" +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><sect3><para> +#: ../en/ch09-hook.xml:1028 msgid "" "While the <filename>usermap</filename> file that it refers to might look like " "this:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:1035 +#: ../en/ch09-hook.xml:1036 msgid "Configuring the text that gets added to a bug" msgstr "配置增加到问题中的正文" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1037 +#: ../en/ch09-hook.xml:1038 msgid "" "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." +"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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1044 +#: ../en/ch09-hook.xml:1045 msgid "" "<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 " @@ -11479,7 +10565,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1058 +#: ../en/ch09-hook.xml:1059 msgid "" "<literal>template</literal>: The text of the template to use. In addition to " "the usual changeset-related variables, this template can use <literal>hgweb</" @@ -11489,25 +10575,25 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1068 +#: ../en/ch09-hook.xml:1069 msgid "" "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</" +"\"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:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1080 +#: ../en/ch09-hook.xml:1081 msgid "" "Here is an example set of <literal role=\"hg-ext\">bugzilla</literal> hook " "config information." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1097 +#: ../en/ch09-hook.xml:1091 msgid "" "The most common problems with configuring the <literal role=\"hg-ext" "\">bugzilla</literal> hook relate to running Bugzilla's " @@ -11516,7 +10602,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1103 +#: ../en/ch09-hook.xml:1097 msgid "" "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 " @@ -11527,7 +10613,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1114 +#: ../en/ch09-hook.xml:1108 msgid "" "You can cause <filename>processmail</filename> to be run with the suitable " "user's identity using the <command>sudo</command> command. Here is an " @@ -11535,7 +10621,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1122 +#: ../en/ch09-hook.xml:1115 msgid "" "This allows the <literal>hg_user</literal> user to run a " "<filename>processmail-wrapper</filename> program under the identity of " @@ -11543,7 +10629,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1127 +#: ../en/ch09-hook.xml:1120 msgid "" "This indirection through a wrapper script is necessary, because " "<filename>processmail</filename> expects to be run with its current directory " @@ -11553,14 +10639,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1136 +#: ../en/ch09-hook.xml:1129 msgid "" "It doesn't seem to matter what email address you pass to " "<filename>processmail</filename>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1140 +#: ../en/ch09-hook.xml:1133 msgid "" "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" @@ -11569,7 +10655,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1148 +#: ../en/ch09-hook.xml:1140 msgid "" "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 " @@ -11578,13 +10664,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1158 +#: ../en/ch09-hook.xml:1150 msgid "" "<literal role=\"hg-ext\">notify</literal>&emdash;send email notifications" msgstr "<literal role=\"hg-ext\">notify</literal>&emdash;邮件通知" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1161 +#: ../en/ch09-hook.xml:1153 msgid "" "Although Mercurial's built-in web server provides RSS feeds of changes in " "every repository, many people prefer to receive change notifications via " @@ -11594,7 +10680,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1169 +#: ../en/ch09-hook.xml:1161 msgid "" "As with the <literal role=\"hg-ext\">bugzilla</literal> hook, the <literal " "role=\"hg-ext\">notify</literal> hook is template-driven, so you can " @@ -11602,7 +10688,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1175 +#: ../en/ch09-hook.xml:1167 msgid "" "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 " @@ -11611,12 +10697,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:1183 +#: ../en/ch09-hook.xml:1175 msgid "Configuring the <literal role=\"hg-ext\">notify</literal> hook" msgstr "配置 <literal role=\"hg-ext\">notify</literal> 钩子" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1186 +#: ../en/ch09-hook.xml:1178 msgid "" "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 " @@ -11624,15 +10710,15 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1197 +#: ../en/ch09-hook.xml:1190 msgid "" "Configuration information for this hook lives in the <literal role=\"rc-notify" -"\">notify</literal> section of a <filename role=\"special\"> /.hgrc</filename>" -"\\ file." +"\">notify</literal> section of a <filename role=\"special\">~/.hgrc</" +"filename> file." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1202 +#: ../en/ch09-hook.xml:1195 msgid "" "<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 " @@ -11644,17 +10730,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1214 +#: ../en/ch09-hook.xml:1207 msgid "" "<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 " +"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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1223 +#: ../en/ch09-hook.xml:1216 msgid "" "<envar role=\"rc-item-notify\">strip</envar>: The number of leading path " "separator characters to strip from a repository's path, when deciding whether " @@ -11669,7 +10755,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1240 +#: ../en/ch09-hook.xml:1233 msgid "" "<envar role=\"rc-item-notify\">template</envar>: The template text to use " "when sending messages. This specifies both the contents of the message " @@ -11677,7 +10763,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1246 +#: ../en/ch09-hook.xml:1239 msgid "" "<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 " @@ -11686,7 +10772,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1255 +#: ../en/ch09-hook.xml:1248 msgid "" "<envar role=\"rc-item-notify\">sources</envar>: A list of sources of " "changesets to consider. This lets you limit <literal role=\"hg-ext\">notify</" @@ -11696,7 +10782,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1267 +#: ../en/ch09-hook.xml:1260 msgid "" "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; " @@ -11704,19 +10790,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1273 +#: ../en/ch09-hook.xml:1266 msgid "" "Here is an example set of <literal role=\"hg-ext\">notify</literal> " "configuration information." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1289 +#: ../en/ch09-hook.xml:1272 msgid "This will produce a message that looks like the following:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1310 +#: ../en/ch09-hook.xml:1282 msgid "" "Do not forget that by default, the <literal role=\"hg-ext\">notify</literal> " "extension <emphasis>will not send any mail</emphasis> until you explicitly " @@ -11726,22 +10812,22 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:1322 +#: ../en/ch09-hook.xml:1294 msgid "Information for writers of hooks" msgstr "编写钩子的信息" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1325 +#: ../en/ch09-hook.xml:1297 msgid "In-process hook execution" msgstr "进程内钩子的执行" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1327 +#: ../en/ch09-hook.xml:1299 msgid "An in-process hook is called with arguments of the following form:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1333 +#: ../en/ch09-hook.xml:1303 msgid "" "The <literal>ui</literal> parameter is a <literal role=\"py-mod-mercurial.ui" "\">ui</literal> object. The <literal>repo</literal> parameter is a <literal " @@ -11751,7 +10837,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1342 +#: ../en/ch09-hook.xml:1312 msgid "" "If a parameter is named <literal>node</literal> or <literal>parentN</" "literal>, it will contain a hexadecimal changeset ID. The empty string is " @@ -11760,21 +10846,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1349 +#: ../en/ch09-hook.xml:1319 msgid "" "If a parameter is named <literal>url</literal>, it will contain the URL of a " "remote repository, if that can be determined." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1354 +#: ../en/ch09-hook.xml:1324 msgid "" "Boolean-valued parameters are represented as Python <literal>bool</literal> " "objects." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1359 +#: ../en/ch09-hook.xml:1329 msgid "" "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 " @@ -11783,7 +10869,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1366 +#: ../en/ch09-hook.xml:1336 msgid "" "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 " @@ -11792,20 +10878,20 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1373 +#: ../en/ch09-hook.xml:1343 msgid "" "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." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1381 +"from hex to binary, use the <literal>bin</literal> function." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><title> +#: ../en/ch09-hook.xml:1351 msgid "External hook execution" msgstr "外部钩子的执行" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1383 +#: ../en/ch09-hook.xml:1353 msgid "" "An external hook is passed to the shell of the user running Mercurial. " "Features of that shell, such as variable substitution and command " @@ -11815,7 +10901,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1391 +#: ../en/ch09-hook.xml:1361 msgid "" "Hook parameters are passed to the hook as environment variables. Each " "environment variable's name is converted in upper case and prefixed with the " @@ -11826,7 +10912,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1400 +#: ../en/ch09-hook.xml:1370 msgid "" "A boolean parameter is represented as the string <quote><literal>1</literal></" "quote> for <quote>true</quote>, <quote><literal>0</literal></quote> for " @@ -11839,19 +10925,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1412 +#: ../en/ch09-hook.xml:1382 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1419 +#: ../en/ch09-hook.xml:1389 msgid "Finding out where changesets come from" msgstr "检查修改集来自何处" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1421 +#: ../en/ch09-hook.xml:1391 msgid "" "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</" @@ -11861,12 +10947,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:1430 +#: ../en/ch09-hook.xml:1400 msgid "Sources of changesets" msgstr "修改集的来源" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1432 +#: ../en/ch09-hook.xml:1402 msgid "" "Mercurial will tell a hook what means are, or were, used to transfer " "changesets between repositories. This is provided by Mercurial in a Python " @@ -11875,40 +10961,40 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1440 +#: ../en/ch09-hook.xml:1410 msgid "" "<literal>serve</literal>: Changesets are transferred to or from a remote " "repository over http or ssh." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1445 +#: ../en/ch09-hook.xml:1415 msgid "" "<literal>pull</literal>: Changesets are being transferred via a pull from one " "repository into another." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1450 +#: ../en/ch09-hook.xml:1420 msgid "" "<literal>push</literal>: Changesets are being transferred via a push from one " "repository into another." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1455 +#: ../en/ch09-hook.xml:1425 msgid "" "<literal>bundle</literal>: Changesets are being transferred to or from a " "bundle." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><title> -#: ../en/ch10-hook.xml:1462 +#: ../en/ch09-hook.xml:1432 msgid "Where changes are going&emdash;remote repository URLs" msgstr "修改集要到哪里&emdash;远程版本库的地址" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1465 +#: ../en/ch09-hook.xml:1435 msgid "" "When possible, Mercurial will tell a hook the location of the <quote>far " "side</quote> of an activity that transfers changeset data between " @@ -11918,7 +11004,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><para> -#: ../en/ch10-hook.xml:1473 +#: ../en/ch09-hook.xml:1443 msgid "" "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 " @@ -11927,14 +11013,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1480 +#: ../en/ch09-hook.xml:1450 msgid "" "<literal>remote:ssh:1.2.3.4</literal>&emdash;remote ssh client, at the IP " "address <literal>1.2.3.4</literal>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1485 +#: ../en/ch09-hook.xml:1455 msgid "" "<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 " @@ -11942,24 +11028,24 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><sect3><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1492 +#: ../en/ch09-hook.xml:1462 msgid "Empty&emdash;no information could be discovered about the remote client." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch10-hook.xml:1501 +#: ../en/ch09-hook.xml:1471 msgid "Hook reference" msgstr "钩子参考" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1504 +#: ../en/ch09-hook.xml:1474 msgid "" "<literal role=\"hook\">changegroup</literal>&emdash;after remote changesets " "added" msgstr "<literal role=\"hook\">changegroup</literal>&emdash;增加远程修改集之后" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1507 +#: ../en/ch09-hook.xml:1477 msgid "" "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> " @@ -11970,7 +11056,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1517 +#: ../en/ch09-hook.xml:1487 msgid "" "Some possible uses for this hook include kicking off an automated build or " "test of the added changesets, updating a bug database, or notifying " @@ -11978,42 +11064,41 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1523 ../en/ch10-hook.xml:1564 ../en/ch10-hook.xml:1607 -#: ../en/ch10-hook.xml:1649 ../en/ch10-hook.xml:1704 ../en/ch10-hook.xml:1744 -#: ../en/ch10-hook.xml:1780 ../en/ch10-hook.xml:1815 ../en/ch10-hook.xml:1877 -#: ../en/ch10-hook.xml:1935 ../en/ch10-hook.xml:1969 ../en/ch10-hook.xml:1997 +#: ../en/ch09-hook.xml:1493 ../en/ch09-hook.xml:1533 ../en/ch09-hook.xml:1576 +#: ../en/ch09-hook.xml:1618 ../en/ch09-hook.xml:1673 ../en/ch09-hook.xml:1713 +#: ../en/ch09-hook.xml:1749 ../en/ch09-hook.xml:1784 ../en/ch09-hook.xml:1846 +#: ../en/ch09-hook.xml:1904 ../en/ch09-hook.xml:1938 ../en/ch09-hook.xml:1966 msgid "Parameters to this hook:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1526 ../en/ch10-hook.xml:1880 +#: ../en/ch09-hook.xml:1496 ../en/ch09-hook.xml:1849 msgid "" "<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>." -msgstr "" - -#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1536 ../en/ch10-hook.xml:1614 ../en/ch10-hook.xml:1707 -#: ../en/ch10-hook.xml:1890 +"<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>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> +#: ../en/ch09-hook.xml:1505 ../en/ch09-hook.xml:1583 ../en/ch09-hook.xml:1676 +#: ../en/ch09-hook.xml:1859 msgid "" "<literal>source</literal>: A string. The source of these changes. See " "section <xref linkend=\"sec.hook.sources\"/> for details." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1541 ../en/ch10-hook.xml:1619 ../en/ch10-hook.xml:1670 -#: ../en/ch10-hook.xml:1712 ../en/ch10-hook.xml:1794 ../en/ch10-hook.xml:1895 +#: ../en/ch09-hook.xml:1510 ../en/ch09-hook.xml:1588 ../en/ch09-hook.xml:1639 +#: ../en/ch09-hook.xml:1681 ../en/ch09-hook.xml:1763 ../en/ch09-hook.xml:1864 msgid "" "<literal>url</literal>: A URL. The location of the remote repository, if " "known. See section <xref linkend=\"sec.hook.url\"/> for more information." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1548 +#: ../en/ch09-hook.xml:1517 msgid "" "See also: <literal role=\"hook\">incoming</literal> (section <xref linkend=" "\"sec.hook.incoming\"/>), <literal role=\"hook\">prechangegroup</literal> " @@ -12023,40 +11108,40 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1558 +#: ../en/ch09-hook.xml:1527 msgid "" "<literal role=\"hook\">commit</literal>&emdash;after a new changeset is " "created" msgstr "<literal role=\"hook\">commit</literal>&emdash;创建新修改集之后" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1561 +#: ../en/ch09-hook.xml:1530 msgid "This hook is run after a new changeset has been created." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1567 ../en/ch10-hook.xml:1938 +#: ../en/ch09-hook.xml:1536 ../en/ch09-hook.xml:1907 msgid "" "<literal>node</literal>: A changeset ID. The changeset ID of the newly " "committed changeset." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1571 ../en/ch10-hook.xml:1942 +#: ../en/ch09-hook.xml:1540 ../en/ch09-hook.xml:1911 msgid "" "<literal>parent1</literal>: A changeset ID. The changeset ID of the first " "parent of the newly committed changeset." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1576 ../en/ch10-hook.xml:1947 +#: ../en/ch09-hook.xml:1545 ../en/ch09-hook.xml:1916 msgid "" "<literal>parent2</literal>: A changeset ID. The changeset ID of the second " "parent of the newly committed changeset." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1582 +#: ../en/ch09-hook.xml:1551 msgid "" "See also: <literal role=\"hook\">precommit</literal> (section <xref linkend=" "\"sec.hook.precommit\"/>), <literal role=\"hook\">pretxncommit</literal> " @@ -12064,14 +11149,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1590 +#: ../en/ch09-hook.xml:1559 msgid "" "<literal role=\"hook\">incoming</literal>&emdash;after one remote changeset " "is added" msgstr "<literal role=\"hook\">incoming</literal>&emdash;增加远程修改集之后" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1593 +#: ../en/ch09-hook.xml:1562 msgid "" "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 " @@ -12080,7 +11165,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1600 +#: ../en/ch09-hook.xml:1569 msgid "" "You can use this hook for the same purposes as the <literal role=\"hook" "\">changegroup</literal> hook (section <xref linkend=\"sec.hook.changegroup\"/" @@ -12089,13 +11174,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1610 +#: ../en/ch09-hook.xml:1579 msgid "" "<literal>node</literal>: A changeset ID. The ID of the newly added changeset." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1626 +#: ../en/ch09-hook.xml:1595 msgid "" "See also: <literal role=\"hook\">changegroup</literal> (section <xref linkend=" "\"sec.hook.changegroup\"/>) <literal role=\"hook\">prechangegroup</literal> " @@ -12105,14 +11190,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1636 +#: ../en/ch09-hook.xml:1605 msgid "" "<literal role=\"hook\">outgoing</literal>&emdash;after changesets are " "propagated" msgstr "<literal role=\"hook\">outgoing</literal>&emdash;传播修改集之后" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1639 +#: ../en/ch09-hook.xml:1608 msgid "" "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 " @@ -12120,21 +11205,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1645 +#: ../en/ch09-hook.xml:1614 msgid "" "One possible use for this hook is to notify administrators that changes have " "been pulled." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1652 +#: ../en/ch09-hook.xml:1621 msgid "" "<literal>node</literal>: A changeset ID. The changeset ID of the first " "changeset of the group that was sent." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1657 +#: ../en/ch09-hook.xml:1626 msgid "" "<literal>source</literal>: A string. The source of the of the operation (see " "section <xref linkend=\"sec.hook.sources\"/>). If a remote client pulled " @@ -12146,14 +11231,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1677 +#: ../en/ch09-hook.xml:1646 msgid "" "See also: <literal role=\"hook\">preoutgoing</literal> (section <xref linkend=" "\"sec.hook.preoutgoing\"/>)" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1683 +#: ../en/ch09-hook.xml:1652 msgid "" "<literal role=\"hook\">prechangegroup</literal>&emdash;before starting to add " "remote changesets" @@ -12161,14 +11246,14 @@ "<literal role=\"hook\">prechangegroup</literal>&emdash;增加远程修改集之前" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1687 +#: ../en/ch09-hook.xml:1656 msgid "" "This controlling hook is run before Mercurial begins to add a group of " "changesets from another repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1691 +#: ../en/ch09-hook.xml:1660 msgid "" "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 " @@ -12176,7 +11261,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1697 +#: ../en/ch09-hook.xml:1666 msgid "" "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 " @@ -12185,7 +11270,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1719 +#: ../en/ch09-hook.xml:1688 msgid "" "See also: <literal role=\"hook\">changegroup</literal> (section <xref linkend=" "\"sec.hook.changegroup\"/>), <literal role=\"hook\">incoming</literal> " @@ -12195,14 +11280,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1729 +#: ../en/ch09-hook.xml:1698 msgid "" "<literal role=\"hook\">precommit</literal>&emdash;before starting to commit a " "changeset" msgstr "<literal role=\"hook\">precommit</literal>&emdash;提交修改集之前" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1732 +#: ../en/ch09-hook.xml:1701 msgid "" "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 " @@ -12210,7 +11295,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1738 +#: ../en/ch09-hook.xml:1707 msgid "" "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, " @@ -12218,28 +11303,28 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1747 +#: ../en/ch09-hook.xml:1716 msgid "" "<literal>parent1</literal>: A changeset ID. The changeset ID of the first " "parent of the working directory." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1752 +#: ../en/ch09-hook.xml:1721 msgid "" "<literal>parent2</literal>: A changeset ID. The changeset ID of the second " "parent of the working directory." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1757 +#: ../en/ch09-hook.xml:1726 msgid "" "If the commit proceeds, the parents of the working directory will become the " "parents of the new changeset." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1761 +#: ../en/ch09-hook.xml:1730 msgid "" "See also: <literal role=\"hook\">commit</literal> (section <xref linkend=" "\"sec.hook.commit\"/>), <literal role=\"hook\">pretxncommit</literal> " @@ -12247,28 +11332,28 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1769 +#: ../en/ch09-hook.xml:1738 msgid "" "<literal role=\"hook\">preoutgoing</literal>&emdash;before starting to " "propagate changesets" msgstr "<literal role=\"hook\">preoutgoing</literal>&emdash;传播修改集之前" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1772 +#: ../en/ch09-hook.xml:1741 msgid "" "This hook is invoked before Mercurial knows the identities of the changesets " "to be transmitted." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1776 +#: ../en/ch09-hook.xml:1745 msgid "" "One use for this hook is to prevent changes from being transmitted to another " "repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1783 +#: ../en/ch09-hook.xml:1752 msgid "" "<literal>source</literal>: A string. The source of the operation that is " "attempting to obtain changes from this repository (see section <xref linkend=" @@ -12279,27 +11364,27 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1801 +#: ../en/ch09-hook.xml:1770 msgid "" "See also: <literal role=\"hook\">outgoing</literal> (section <xref linkend=" "\"sec.hook.outgoing\"/>)" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1807 +#: ../en/ch09-hook.xml:1776 msgid "" "<literal role=\"hook\">pretag</literal>&emdash;before tagging a changeset" msgstr "<literal role=\"hook\">pretag</literal>&emdash;创建标签之前" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1810 +#: ../en/ch09-hook.xml:1779 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1818 +#: ../en/ch09-hook.xml:1787 msgid "" "<literal>local</literal>: A boolean. Whether the tag is local to this " "repository instance (i.e. stored in <filename role=\"special\">.hg/localtags</" @@ -12308,19 +11393,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1825 +#: ../en/ch09-hook.xml:1794 msgid "" "<literal>node</literal>: A changeset ID. The ID of the changeset to be " "tagged." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1829 +#: ../en/ch09-hook.xml:1798 msgid "<literal>tag</literal>: A string. The name of the tag to be created." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1834 +#: ../en/ch09-hook.xml:1803 msgid "" "If the tag to be created is revision-controlled, the <literal role=\"hook" "\">precommit</literal> and <literal role=\"hook\">pretxncommit</literal> " @@ -12329,14 +11414,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1841 +#: ../en/ch09-hook.xml:1810 msgid "" "See also: <literal role=\"hook\">tag</literal> (section <xref linkend=\"sec." "hook.tag\"/>)" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1846 +#: ../en/ch09-hook.xml:1815 msgid "" "<literal role=\"hook\">pretxnchangegroup</literal>&emdash;before completing " "addition of remote changesets" @@ -12345,7 +11430,7 @@ "前" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1850 +#: ../en/ch09-hook.xml:1819 msgid "" "This controlling hook is run before a transaction&emdash;that manages the " "addition of a group of new changesets from outside the repository&emdash;" @@ -12355,7 +11440,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1859 +#: ../en/ch09-hook.xml:1828 msgid "" "This hook can access the metadata associated with the almost-added " "changesets, but it should not do anything permanent with this data. It must " @@ -12363,7 +11448,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1865 +#: ../en/ch09-hook.xml:1834 msgid "" "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 " @@ -12372,7 +11457,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1872 +#: ../en/ch09-hook.xml:1841 msgid "" "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 " @@ -12380,7 +11465,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1902 +#: ../en/ch09-hook.xml:1871 msgid "" "See also: <literal role=\"hook\">changegroup</literal> (section <xref linkend=" "\"sec.hook.changegroup\"/>), <literal role=\"hook\">incoming</literal> " @@ -12390,14 +11475,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1912 +#: ../en/ch09-hook.xml:1881 msgid "" "<literal role=\"hook\">pretxncommit</literal>&emdash;before completing commit " "of new changeset" msgstr "<literal role=\"hook\">pretxncommit</literal>&emdash;完成提交之前" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1915 +#: ../en/ch09-hook.xml:1884 msgid "" "This controlling hook is run before a transaction&emdash;that manages a new " "commit&emdash;completes. If the hook succeeds, the transaction completes and " @@ -12406,7 +11491,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1923 +#: ../en/ch09-hook.xml:1892 msgid "" "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 " @@ -12414,7 +11499,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1929 +#: ../en/ch09-hook.xml:1898 msgid "" "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 " @@ -12423,14 +11508,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1953 +#: ../en/ch09-hook.xml:1922 msgid "" "See also: <literal role=\"hook\">precommit</literal> (section <xref linkend=" "\"sec.hook.precommit\"/>)" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1959 +#: ../en/ch09-hook.xml:1928 msgid "" "<literal role=\"hook\">preupdate</literal>&emdash;before updating or merging " "working directory" @@ -12438,7 +11523,7 @@ "<literal role=\"hook\">preupdate</literal>&emdash;更新或合并工作目录之前" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1962 +#: ../en/ch09-hook.xml:1931 msgid "" "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 " @@ -12447,7 +11532,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1972 +#: ../en/ch09-hook.xml:1941 msgid "" "<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 " @@ -12455,7 +11540,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:1978 +#: ../en/ch09-hook.xml:1947 msgid "" "<literal>parent2</literal>: A changeset ID. Only set if the working " "directory is being merged. The ID of the revision that the working directory " @@ -12463,24 +11548,24 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1985 +#: ../en/ch09-hook.xml:1954 msgid "" "See also: <literal role=\"hook\">update</literal> (section <xref linkend=" "\"sec.hook.update\"/>)" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:1991 +#: ../en/ch09-hook.xml:1960 msgid "<literal role=\"hook\">tag</literal>&emdash;after tagging a changeset" msgstr "<literal role=\"hook\">tag</literal>&emdash;创建标签之后" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:1994 +#: ../en/ch09-hook.xml:1963 msgid "This hook is run after a tag has been created." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:2000 +#: ../en/ch09-hook.xml:1969 msgid "" "<literal>local</literal>: A boolean. Whether the new tag is local to this " "repository instance (i.e. stored in <filename role=\"special\">.hg/" @@ -12489,19 +11574,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:2008 +#: ../en/ch09-hook.xml:1977 msgid "" "<literal>node</literal>: A changeset ID. The ID of the changeset that was " "tagged." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:2012 +#: ../en/ch09-hook.xml:1981 msgid "<literal>tag</literal>: A string. The name of the tag that was created." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:2017 +#: ../en/ch09-hook.xml:1986 msgid "" "If the created tag is revision-controlled, the <literal role=\"hook\">commit</" "literal> hook (section <xref linkend=\"sec.hook.commit\"/>) is run before " @@ -12509,21 +11594,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:2022 +#: ../en/ch09-hook.xml:1991 msgid "" "See also: <literal role=\"hook\">pretag</literal> (section <xref linkend=" "\"sec.hook.pretag\"/>)" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch10-hook.xml:2028 +#: ../en/ch09-hook.xml:1997 msgid "" "<literal role=\"hook\">update</literal>&emdash;after updating or merging " "working directory" msgstr "<literal role=\"hook\">update</literal>&emdash;更新或合并工作目录之后" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:2031 +#: ../en/ch09-hook.xml:2000 msgid "" "This hook is run after an update or merge of the working directory " "completes. Since a merge can fail (if the external <command>hgmerge</" @@ -12532,14 +11617,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:2039 +#: ../en/ch09-hook.xml:2008 msgid "" "<literal>error</literal>: A boolean. Indicates whether the update or merge " "completed successfully." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:2044 +#: ../en/ch09-hook.xml:2013 msgid "" "<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 " @@ -12547,7 +11632,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch10-hook.xml:2050 +#: ../en/ch09-hook.xml:2019 msgid "" "<literal>parent2</literal>: A changeset ID. Only set if the working " "directory was merged. The ID of the revision that the working directory was " @@ -12555,19 +11640,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch10-hook.xml:2056 +#: ../en/ch09-hook.xml:2025 msgid "" "See also: <literal role=\"hook\">preupdate</literal> (section <xref linkend=" "\"sec.hook.preupdate\"/>)" msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch11-template.xml:5 +#: ../en/ch10-template.xml:5 msgid "Customising the output of Mercurial" msgstr "定制 Mercurial 的输出" #. type: Content of: <book><chapter><para> -#: ../en/ch11-template.xml:7 +#: ../en/ch10-template.xml:7 msgid "" "Mercurial provides a powerful mechanism to let you control how it displays " "information. The mechanism is based on templates. You can use templates to " @@ -12576,12 +11661,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:14 +#: ../en/ch10-template.xml:14 msgid "Using precanned output styles" msgstr "使用预定义的输出样式" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:16 +#: ../en/ch10-template.xml:16 msgid "" "Packaged with Mercurial are some output styles that you can use immediately. " "A style is simply a precanned template that someone wrote and installed " @@ -12589,14 +11674,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:21 +#: ../en/ch10-template.xml:21 msgid "" "Before we take a look at Mercurial's bundled styles, let's review its normal " "output." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:26 +#: ../en/ch10-template.xml:26 msgid "" "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 " @@ -12604,7 +11689,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:33 +#: ../en/ch10-template.xml:33 msgid "" "The <literal>changelog</literal> style hints at the expressive power of " "Mercurial's templating engine. This style attempts to follow the GNU " @@ -12612,27 +11697,27 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:40 +#: ../en/ch10-template.xml:40 msgid "" "You will not be shocked to learn that Mercurial's default output style is " "named <literal>default</literal>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch11-template.xml:44 +#: ../en/ch10-template.xml:44 msgid "Setting a default style" msgstr "设置默认样式" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:46 +#: ../en/ch10-template.xml:46 msgid "" "You can modify the output style that Mercurial will use for every command by " -"editing your <filename role=\"special\"> /.hgrc</filename>\\ file, naming the " +"editing your <filename role=\"special\">~/.hgrc</filename> file, naming the " "style you would prefer to use." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:53 +#: ../en/ch10-template.xml:54 msgid "" "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 " @@ -12641,12 +11726,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:62 +#: ../en/ch10-template.xml:63 msgid "Commands that support styles and templates" msgstr "支持样式和模版的命令" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:64 +#: ../en/ch10-template.xml:65 msgid "" "All of Mercurial's <quote><literal>log</literal>-like</quote> commands let " "you use styles and templates: <command role=\"hg-cmd\">hg incoming</command>, " @@ -12655,7 +11740,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:71 +#: ../en/ch10-template.xml:72 msgid "" "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 " @@ -12664,12 +11749,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:79 +#: ../en/ch10-template.xml:80 msgid "The basics of templating" msgstr "模版基础" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:81 +#: ../en/ch10-template.xml:82 msgid "" "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 " @@ -12677,20 +11762,20 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:86 +#: ../en/ch10-template.xml:87 msgid "" "Before we continue, let's look again at a simple example of Mercurial's " "normal output." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:91 +#: ../en/ch10-template.xml:92 msgid "" "Now, let's run the same command, but using a template to change its output." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:96 +#: ../en/ch10-template.xml:97 msgid "" "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-" @@ -12700,7 +11785,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:104 +#: ../en/ch10-template.xml:105 msgid "" "Notice that the template string above ends with the text <quote><literal>\\n</" "literal></quote>. This is an <emphasis>escape sequence</emphasis>, telling " @@ -12710,14 +11795,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:112 +#: ../en/ch10-template.xml:113 msgid "" "A template that prints a fixed string of text all the time isn't very useful; " "let's try something a bit more complex." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:118 +#: ../en/ch10-template.xml:119 msgid "" "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 " @@ -12729,25 +11814,25 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:131 +#: ../en/ch10-template.xml:132 msgid "Common template keywords" msgstr "模版关键字" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:133 +#: ../en/ch10-template.xml:134 msgid "" "You can start writing simple templates immediately using the keywords below." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:137 +#: ../en/ch10-template.xml:138 msgid "" "<literal role=\"template-keyword\">author</literal>: String. The unmodified " "author of the changeset." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:141 +#: ../en/ch10-template.xml:142 msgid "" "<literal role=\"template-keyword\">branches</literal>: String. The name of " "the branch on which the changeset was committed. Will be empty if the branch " @@ -12755,7 +11840,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:147 +#: ../en/ch10-template.xml:148 msgid "" "<literal role=\"template-keyword\">date</literal>: Date information. The " "date when the changeset was committed. This is <emphasis>not</emphasis> " @@ -12767,70 +11852,70 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:158 +#: ../en/ch10-template.xml:159 msgid "" "<literal role=\"template-keyword\">desc</literal>: String. The text of the " "changeset description." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:161 +#: ../en/ch10-template.xml:162 msgid "" "<literal role=\"template-keyword\">files</literal>: List of strings. All " "files modified, added, or removed by this changeset." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:166 +#: ../en/ch10-template.xml:167 msgid "" "<literal role=\"template-keyword\">file_adds</literal>: List of strings. " "Files added by this changeset." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:170 +#: ../en/ch10-template.xml:171 msgid "" "<literal role=\"template-keyword\">file_dels</literal>: List of strings. " "Files removed by this changeset." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:174 +#: ../en/ch10-template.xml:175 msgid "" "<literal role=\"template-keyword\">node</literal>: String. The changeset " "identification hash, as a 40-character hexadecimal string." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:178 +#: ../en/ch10-template.xml:179 msgid "" "<literal role=\"template-keyword\">parents</literal>: List of strings. The " "parents of the changeset." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:182 +#: ../en/ch10-template.xml:183 msgid "" "<literal role=\"template-keyword\">rev</literal>: Integer. The repository-" "local changeset revision number." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:186 +#: ../en/ch10-template.xml:187 msgid "" "<literal role=\"template-keyword\">tags</literal>: List of strings. Any tags " "associated with the changeset." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:191 +#: ../en/ch10-template.xml:192 msgid "" "A few simple experiments will show us what to expect when we use these " "keywords; you can see the results below." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:196 +#: ../en/ch10-template.xml:197 msgid "" "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</" @@ -12839,12 +11924,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:206 +#: ../en/ch10-template.xml:207 msgid "Escape sequences" msgstr "转义序列" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:208 +#: ../en/ch10-template.xml:209 msgid "" "Mercurial's templating engine recognises the most commonly used escape " "sequences in strings. When it sees a backslash (<quote><literal>\\</" @@ -12853,48 +11938,48 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:215 -msgid "" -"<literal>\\textbackslash\\textbackslash</literal>: Backslash, <quote><literal>" -"\\</literal></quote>, ASCII 134." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:219 -msgid "<literal>\\textbackslash n</literal>: Newline, ASCII 12." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:222 -msgid "<literal>\\textbackslash r</literal>: Carriage return, ASCII 15." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:225 -msgid "<literal>\\textbackslash t</literal>: Tab, ASCII 11." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:228 -msgid "<literal>\\textbackslash v</literal>: Vertical tab, ASCII 13." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:231 -msgid "" -"<literal>\\textbackslash {</literal>: Open curly brace, <quote><literal>{</" -"literal></quote>, ASCII 173." -msgstr "" - -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:235 -msgid "" -"<literal>\\textbackslash }</literal>: Close curly brace, <quote><literal>}</" -"literal></quote>, ASCII 175." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:240 +#: ../en/ch10-template.xml:216 +msgid "" +"<literal>\\</literal>: Backslash, <quote><literal>\\</literal></quote>, ASCII " +"134." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:220 +msgid "<literal>\\n</literal>: Newline, ASCII 12." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:223 +msgid "<literal>\\r</literal>: Carriage return, ASCII 15." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:226 +msgid "<literal>\\t</literal>: Tab, ASCII 11." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:229 +msgid "<literal>\\v</literal>: Vertical tab, ASCII 13." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:232 +msgid "" +"<literal>{</literal>: Open curly brace, <quote><literal>{</literal></quote>, " +"ASCII 173." +msgstr "" + +#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> +#: ../en/ch10-template.xml:236 +msgid "" +"<literal>}</literal>: Close curly brace, <quote><literal>}</literal></quote>, " +"ASCII 175." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch10-template.xml:241 msgid "" "As indicated above, if you want the expansion of a template to contain a " "literal <quote><literal>\\</literal></quote>, <quote><literal>{</literal></" @@ -12902,12 +11987,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:248 +#: ../en/ch10-template.xml:249 msgid "Filtering keywords to change their results" msgstr "通过过滤关键字来修改输出结果" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:250 +#: ../en/ch10-template.xml:251 msgid "" "Some of the results of template expansion are not immediately easy to use. " "Mercurial lets you specify an optional chain of <emphasis>filters</emphasis> " @@ -12917,7 +12002,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:257 +#: ../en/ch10-template.xml:258 msgid "" "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 " @@ -12926,7 +12011,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:264 +#: ../en/ch10-template.xml:265 msgid "" "<literal role=\"template-filter\">addbreaks</literal>: Any text. Add an XHTML " "<quote><literal><br/></literal></quote> tag before the end of every " @@ -12935,7 +12020,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:271 +#: ../en/ch10-template.xml:272 msgid "" "<literal role=\"template-kw-filt-date\">age</literal>: <literal role=" "\"template-keyword\">date</literal> keyword. Render the age of the date, " @@ -12944,7 +12029,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:278 +#: ../en/ch10-template.xml:279 msgid "" "<literal role=\"template-filter\">basename</literal>: Any text, but most " "useful for the <literal role=\"template-keyword\">files</literal> keyword and " @@ -12954,7 +12039,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:287 +#: ../en/ch10-template.xml:288 msgid "" "<literal role=\"template-kw-filt-date\">date</literal>: <literal role=" "\"template-keyword\">date</literal> keyword. Render a date in a similar " @@ -12964,7 +12049,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:295 +#: ../en/ch10-template.xml:296 msgid "" "<literal role=\"template-kw-filt-author\">domain</literal>: Any text, but " "most useful for the <literal role=\"template-keyword\">author</literal> " @@ -12975,7 +12060,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:305 +#: ../en/ch10-template.xml:306 msgid "" "<literal role=\"template-kw-filt-author\">email</literal>: Any text, but most " "useful for the <literal role=\"template-keyword\">author</literal> keyword. " @@ -12985,7 +12070,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:314 +#: ../en/ch10-template.xml:315 msgid "" "<literal role=\"template-filter\">escape</literal>: Any text. Replace the " "special XML/XHTML characters <quote><literal>&</literal></quote>, " @@ -12994,7 +12079,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:322 +#: ../en/ch10-template.xml:323 msgid "" "<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 " @@ -13003,21 +12088,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:330 +#: ../en/ch10-template.xml:331 msgid "" "<literal role=\"template-filter\">fill76</literal>: Any text. Wrap the text " "to fit in 76 columns." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:334 +#: ../en/ch10-template.xml:335 msgid "" "<literal role=\"template-filter\">firstline</literal>: Any text. Yield the " "first line of text, without any trailing newlines." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:339 +#: ../en/ch10-template.xml:340 msgid "" "<literal role=\"template-kw-filt-date\">hgdate</literal>: <literal role=" "\"template-keyword\">date</literal> keyword. Render the date as a pair of " @@ -13026,7 +12111,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:346 +#: ../en/ch10-template.xml:347 msgid "" "<literal role=\"template-kw-filt-date\">isodate</literal>: <literal role=" "\"template-keyword\">date</literal> keyword. Render the date as a text " @@ -13035,7 +12120,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:353 +#: ../en/ch10-template.xml:354 msgid "" "<literal role=\"template-filter\">obfuscate</literal>: Any text, but most " "useful for the <literal role=\"template-keyword\">author</literal> keyword. " @@ -13044,7 +12129,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:361 +#: ../en/ch10-template.xml:362 msgid "" "<literal role=\"template-kw-filt-author\">person</literal>: Any text, but " "most useful for the <literal role=\"template-keyword\">author</literal> " @@ -13054,7 +12139,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:370 +#: ../en/ch10-template.xml:371 msgid "" "<literal role=\"template-kw-filt-date\">rfc822date</literal>: <literal role=" "\"template-keyword\">date</literal> keyword. Render a date using the same " @@ -13063,7 +12148,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:377 +#: ../en/ch10-template.xml:378 msgid "" "<literal role=\"template-kw-filt-node\">short</literal>: Changeset hash. " "Yield the short form of a changeset hash, i.e. a 12-character hexadecimal " @@ -13071,7 +12156,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:382 +#: ../en/ch10-template.xml:383 msgid "" "<literal role=\"template-kw-filt-date\">shortdate</literal>: <literal role=" "\"template-keyword\">date</literal> keyword. Render the year, month, and day " @@ -13080,21 +12165,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:388 +#: ../en/ch10-template.xml:389 msgid "" "<literal role=\"template-filter\">strip</literal>: Any text. Strip all " "leading and trailing whitespace from the string." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:392 +#: ../en/ch10-template.xml:393 msgid "" "<literal role=\"template-filter\">tabindent</literal>: Any text. Yield the " "text, with every line except the first starting with a tab character." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:397 +#: ../en/ch10-template.xml:398 msgid "" "<literal role=\"template-filter\">urlescape</literal>: Any text. Escape all " "characters that are considered <quote>special</quote> by URL parsers. For " @@ -13102,7 +12187,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:404 +#: ../en/ch10-template.xml:405 msgid "" "<literal role=\"template-kw-filt-author\">user</literal>: Any text, but most " "useful for the <literal role=\"template-keyword\">author</literal> keyword. " @@ -13112,7 +12197,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><note><para> -#: ../en/ch11-template.xml:417 +#: ../en/ch10-template.xml:418 msgid "" "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 " @@ -13122,12 +12207,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch11-template.xml:426 +#: ../en/ch10-template.xml:427 msgid "Combining filters" msgstr "组合过滤器" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:428 +#: ../en/ch10-template.xml:429 msgid "" "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 " @@ -13137,7 +12222,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:437 +#: ../en/ch10-template.xml:438 msgid "" "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 " @@ -13146,7 +12231,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:443 +#: ../en/ch10-template.xml:444 msgid "" "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 " @@ -13156,12 +12241,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:454 +#: ../en/ch10-template.xml:455 msgid "From templates to styles" msgstr "从模版到样式" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:456 +#: ../en/ch10-template.xml:457 msgid "" "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 " @@ -13170,7 +12255,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:461 +#: ../en/ch10-template.xml:462 msgid "" "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 " @@ -13178,49 +12263,49 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch11-template.xml:467 +#: ../en/ch10-template.xml:468 msgid "The simplest of style files" msgstr "最简单的样式文件" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:469 +#: ../en/ch10-template.xml:470 msgid "Our simple style file contains just one line:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:473 +#: ../en/ch10-template.xml:474 msgid "" "This tells Mercurial, <quote>if you're printing a changeset, use the text on " "the right as the template</quote>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch11-template.xml:479 +#: ../en/ch10-template.xml:480 msgid "Style file syntax" msgstr "样式文件语法" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:481 +#: ../en/ch10-template.xml:482 msgid "The syntax rules for a style file are simple." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:484 +#: ../en/ch10-template.xml:485 msgid "The file is processed one line at a time." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:487 +#: ../en/ch10-template.xml:488 msgid "Leading and trailing white space are ignored." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:490 +#: ../en/ch10-template.xml:491 msgid "Empty lines are skipped." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:492 +#: ../en/ch10-template.xml:493 msgid "" "If a line starts with either of the characters <quote><literal>#</literal></" "quote> or <quote><literal>;</literal></quote>, the entire line is treated as " @@ -13228,7 +12313,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:497 +#: ../en/ch10-template.xml:498 msgid "" "A line starts with a keyword. This must start with an alphabetic character " "or underscore, and can subsequently contain any alphanumeric character or " @@ -13237,21 +12322,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:503 +#: ../en/ch10-template.xml:504 msgid "" "The next element must be an <quote><literal>=</literal></quote> character, " "which can be preceded or followed by an arbitrary amount of white space." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:508 +#: ../en/ch10-template.xml:509 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:512 +#: ../en/ch10-template.xml:513 msgid "" "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 " @@ -13259,12 +12344,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch11-template.xml:521 +#: ../en/ch10-template.xml:522 msgid "Style files by example" msgstr "样式文件例子" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch11-template.xml:523 +#: ../en/ch10-template.xml:524 msgid "" "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 " @@ -13273,12 +12358,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch11-template.xml:530 +#: ../en/ch10-template.xml:531 msgid "Identifying mistakes in style files" msgstr "在样式文件中定位错误" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:532 +#: ../en/ch10-template.xml:533 msgid "" "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 " @@ -13286,7 +12371,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:538 +#: ../en/ch10-template.xml:539 msgid "" "Notice that <filename>broken.style</filename> attempts to define a " "<literal>changeset</literal> keyword, but forgets to give any content for it. " @@ -13294,35 +12379,35 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:545 +#: ../en/ch10-template.xml:546 msgid "This error message looks intimidating, but it is not too hard to follow." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:549 +#: ../en/ch10-template.xml:550 msgid "" "The first component is simply Mercurial's way of saying <quote>I am giving " "up</quote>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:554 +#: ../en/ch10-template.xml:554 msgid "Next comes the name of the style file that contains the error." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:560 +#: ../en/ch10-template.xml:558 msgid "" "Following the file name is the line number where the error was encountered." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:565 +#: ../en/ch10-template.xml:562 msgid "Finally, a description of what went wrong." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:570 +#: ../en/ch10-template.xml:566 msgid "" "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 " @@ -13330,12 +12415,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch11-template.xml:578 +#: ../en/ch10-template.xml:574 msgid "Uniquely identifying a repository" msgstr "版本库的唯一标识" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:580 +#: ../en/ch10-template.xml:576 msgid "" "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 " @@ -13343,21 +12428,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:587 +#: ../en/ch10-template.xml:583 msgid "" "This is not guaranteed to be unique, but it is nevertheless useful in many " "cases." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:590 +#: ../en/ch10-template.xml:586 msgid "" "It will not work in a completely empty repository, because such a repository " "does not have a revision zero." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:594 +#: ../en/ch10-template.xml:590 msgid "" "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 " @@ -13365,18 +12450,18 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:599 +#: ../en/ch10-template.xml:595 msgid "Here are some uses to which you could put this identifier:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:602 +#: ../en/ch10-template.xml:598 msgid "" "As a key into a table for a database that manages repositories on a server." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:605 +#: ../en/ch10-template.xml:601 msgid "" "As half of a {<emphasis>repository ID</emphasis>, <emphasis>revision ID</" "emphasis>} tuple. Save this information away when you run an automated build " @@ -13385,13 +12470,13 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch11-template.xml:614 +#: ../en/ch10-template.xml:610 msgid "Mimicking Subversion's output" msgstr "模仿 Subversion 的输出" # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:616 +#: ../en/ch10-template.xml:612 msgid "" "Let's try to emulate the default output format used by another revision " "control tool, Subversion." @@ -13399,7 +12484,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:621 +#: ../en/ch10-template.xml:617 msgid "" "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 " @@ -13407,14 +12492,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:628 +#: ../en/ch10-template.xml:624 msgid "" "There are a few small ways in which this template deviates from the output " "produced by Subversion." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:631 +#: ../en/ch10-template.xml:627 msgid "" "Subversion prints a <quote>readable</quote> date (the <quote><literal>Wed, 27 " "Sep 2006</literal></quote> in the example output above) in parentheses. " @@ -13423,7 +12508,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:638 +#: ../en/ch10-template.xml:634 msgid "" "We emulate Subversion's printing of <quote>separator</quote> lines full of " "<quote><literal>-</literal></quote> characters by ending the template with " @@ -13433,7 +12518,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch11-template.xml:647 +#: ../en/ch10-template.xml:643 msgid "" "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 " @@ -13443,7 +12528,7 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:653 +#: ../en/ch10-template.xml:649 msgid "" "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 " @@ -13451,7 +12536,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch11-template.xml:660 +#: ../en/ch10-template.xml:656 msgid "" "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 " @@ -13464,17 +12549,17 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch12-mq.xml:5 +#: ../en/ch11-mq.xml:5 msgid "Managing change with Mercurial Queues" msgstr "使用 MQ 管理修改" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:8 +#: ../en/ch11-mq.xml:8 msgid "The patch management problem" msgstr "补丁的管理问题" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:10 +#: ../en/ch11-mq.xml:10 msgid "" "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 " @@ -13486,7 +12571,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:20 +#: ../en/ch11-mq.xml:20 msgid "" "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 " @@ -13496,7 +12581,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:27 +#: ../en/ch11-mq.xml:27 msgid "" "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 " @@ -13504,7 +12589,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:32 +#: ../en/ch11-mq.xml:32 msgid "" "Distributors of operating systems that include open source software often " "need to make changes to the packages they distribute so that they will build " @@ -13512,7 +12597,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:37 +#: ../en/ch11-mq.xml:37 msgid "" "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> " @@ -13529,7 +12614,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:52 +#: ../en/ch11-mq.xml:52 msgid "" "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 " @@ -13539,7 +12624,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:59 +#: ../en/ch11-mq.xml:59 msgid "" "Fortunately, Mercurial includes a powerful extension, Mercurial Queues (or " "simply <quote>MQ</quote>), that massively simplifies the patch management " @@ -13547,12 +12632,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:65 +#: ../en/ch11-mq.xml:65 msgid "The prehistory of Mercurial Queues" msgstr "MQ 的历史" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:67 +#: ../en/ch11-mq.xml:67 msgid "" "During the late 1990s, several Linux kernel developers started to maintain " "<quote>patch series</quote> that modified the behaviour of the Linux kernel. " @@ -13561,7 +12646,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:73 +#: ../en/ch11-mq.xml:73 msgid "" "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 " @@ -13570,12 +12655,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:80 +#: ../en/ch11-mq.xml:80 msgid "A patchwork quilt" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:82 +#: ../en/ch11-mq.xml:82 msgid "" "In early 2003, Andreas Gruenbacher and Martin Quinson borrowed the approach " "of Andrew's scripts and published a tool called <quote>patchwork quilt</" @@ -13586,7 +12671,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:91 +#: ../en/ch11-mq.xml:91 msgid "" "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 " @@ -13596,7 +12681,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:99 +#: ../en/ch11-mq.xml:99 msgid "" "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 " @@ -13606,7 +12691,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:106 +#: ../en/ch11-mq.xml:106 msgid "" "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 " @@ -13620,19 +12705,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:118 +#: ../en/ch11-mq.xml:118 msgid "" "Quilt knows nothing about revision control tools, so it works equally well on " "top of an unpacked tarball or a Subversion working copy." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:124 +#: ../en/ch11-mq.xml:124 msgid "From patchwork quilt to Mercurial Queues" msgstr "从 patchwork quilt 到 MQ" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:126 +#: ../en/ch11-mq.xml:126 msgid "" "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 " @@ -13640,7 +12725,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:130 +#: ../en/ch11-mq.xml:130 msgid "" "The key difference between quilt and MQ is that quilt knows nothing about " "revision control systems, while MQ is <emphasis>integrated</emphasis> into " @@ -13649,7 +12734,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:136 +#: ../en/ch11-mq.xml:136 msgid "" "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 " @@ -13657,19 +12742,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:144 +#: ../en/ch11-mq.xml:144 msgid "The huge advantage of MQ" msgstr "MQ 的巨大优势" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:146 +#: ../en/ch11-mq.xml:146 msgid "" "I cannot overstate the value that MQ offers through the unification of " "patches and revision control." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:149 +#: ../en/ch11-mq.xml:149 msgid "" "A major reason that patches have persisted in the free software and open " "source world&emdash;in spite of the availability of increasingly capable " @@ -13678,7 +12763,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:155 +#: ../en/ch11-mq.xml:155 msgid "" "Traditional revision control tools make a permanent, irreversible record of " "everything that you do. While this has great value, it's also somewhat " @@ -13689,7 +12774,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:163 +#: ../en/ch11-mq.xml:163 msgid "" "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 " @@ -13700,7 +12785,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:171 +#: ../en/ch11-mq.xml:171 msgid "" "As an example, the integration of patches with revision control makes " "understanding patches and debugging their effects&emdash;and their interplay " @@ -13715,19 +12800,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:187 +#: ../en/ch11-mq.xml:187 msgid "Understanding patches" msgstr "理解补丁" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:189 +#: ../en/ch11-mq.xml:189 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:193 +#: ../en/ch11-mq.xml:193 msgid "" "The traditional Unix <command>diff</command> command compares two files, and " "prints a list of differences between them. The <command>patch</command> " @@ -13737,7 +12822,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:202 +#: ../en/ch11-mq.xml:202 msgid "" "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</" @@ -13746,7 +12831,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:208 +#: ../en/ch11-mq.xml:208 msgid "" "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 " @@ -13756,7 +12841,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:215 +#: ../en/ch11-mq.xml:215 msgid "" "MQ works with <emphasis>unified</emphasis> diffs (<command>patch</command> " "can accept several other diff formats, but MQ doesn't). A unified diff " @@ -13767,7 +12852,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:223 +#: ../en/ch11-mq.xml:223 msgid "" "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 " @@ -13780,7 +12865,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:235 +#: ../en/ch11-mq.xml:235 msgid "" "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 " @@ -13790,7 +12875,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:243 +#: ../en/ch11-mq.xml:243 msgid "" "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 " @@ -13798,12 +12883,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:250 +#: ../en/ch11-mq.xml:250 msgid "Getting started with Mercurial Queues" msgstr "开始使用 MQ" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:252 +#: ../en/ch11-mq.xml:252 msgid "" "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 " @@ -13812,7 +12897,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:261 +#: ../en/ch11-mq.xml:262 msgid "" "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 " @@ -13821,7 +12906,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:269 +#: ../en/ch11-mq.xml:270 msgid "" "You can use MQ with <emphasis>any</emphasis> Mercurial repository, and its " "commands only operate within that repository. To get started, simply prepare " @@ -13829,7 +12914,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:276 +#: ../en/ch11-mq.xml:277 msgid "" "This command creates an empty directory called <filename role=\"special\" " "class=\"directory\">.hg/patches</filename>, where MQ will keep its metadata. " @@ -13838,12 +12923,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:283 +#: ../en/ch11-mq.xml:284 msgid "Creating a new patch" msgstr "创建新补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:285 +#: ../en/ch11-mq.xml:286 msgid "" "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 " @@ -13851,14 +12936,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:289 +#: ../en/ch11-mq.xml:290 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:296 +#: ../en/ch11-mq.xml:297 msgid "" "Also newly present in the <filename role=\"special\" class=\"directory\">.hg/" "patches</filename> directory are two other files, <filename role=\"special" @@ -13871,7 +12956,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch12-mq.xml:308 +#: ../en/ch11-mq.xml:309 msgid "" "You may sometimes want to edit the <filename role=\"special\">series</" "filename> file by hand; for example, to change the sequence in which some " @@ -13881,7 +12966,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:317 +#: ../en/ch11-mq.xml:318 msgid "" "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 " @@ -13890,12 +12975,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:325 +#: ../en/ch11-mq.xml:326 msgid "Refreshing a patch" msgstr "刷新补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:327 +#: ../en/ch11-mq.xml:328 msgid "" "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 " @@ -13903,14 +12988,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:333 +#: ../en/ch11-mq.xml:334 msgid "" "This command folds the changes you have made in the working directory into " "your patch, and updates its corresponding changeset to contain those changes." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:337 +#: ../en/ch11-mq.xml:338 msgid "" "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 " @@ -13920,12 +13005,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:348 +#: ../en/ch11-mq.xml:349 msgid "Stacking and tracking patches" msgstr "堆叠和跟踪补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:350 +#: ../en/ch11-mq.xml:351 msgid "" "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 " @@ -13934,7 +13019,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:357 +#: ../en/ch11-mq.xml:358 msgid "" "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" @@ -13942,7 +13027,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:362 +#: ../en/ch11-mq.xml:363 msgid "" "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 " @@ -13951,7 +13036,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:372 +#: ../en/ch11-mq.xml:373 msgid "" "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 " @@ -13959,7 +13044,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:378 +#: ../en/ch11-mq.xml:379 msgid "" "The <command role=\"hg-ext-mq\">qapplied</command> command lists every patch " "that MQ has <emphasis>applied</emphasis> in this repository, again from " @@ -13967,12 +13052,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:387 +#: ../en/ch11-mq.xml:388 msgid "Manipulating the patch stack" msgstr "操作补丁堆栈" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:389 +#: ../en/ch11-mq.xml:390 msgid "" "The previous discussion implied that there must be a difference between " "<quote>known</quote> and <quote>applied</quote> patches, and there is. MQ " @@ -13980,7 +13065,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:394 +#: ../en/ch11-mq.xml:395 msgid "" "An <emphasis>applied</emphasis> patch has a corresponding changeset in the " "repository, and the effects of the patch and changeset are visible in the " @@ -13994,17 +13079,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject> -#: ../en/ch12-mq.xml:408 +#: ../en/ch11-mq.xml:409 msgid "<imageobject><imagedata fileref=\"images/mq-stack.png\"/></imageobject>" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><informalfigure><mediaobject><caption><para> -#: ../en/ch12-mq.xml:410 +#: ../en/ch11-mq.xml:411 msgid "Applied and unapplied patches in the MQ patch stack" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:415 +#: ../en/ch11-mq.xml:416 msgid "" "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 " @@ -14014,7 +13099,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:424 +#: ../en/ch11-mq.xml:425 msgid "" "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 " @@ -14022,12 +13107,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:432 +#: ../en/ch11-mq.xml:433 msgid "Pushing and popping many patches" msgstr "压入或弹出多个补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:434 +#: ../en/ch11-mq.xml:435 msgid "" "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 " @@ -14040,12 +13125,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:451 +#: ../en/ch11-mq.xml:452 msgid "Safety checks, and overriding them" msgstr "安全的检查,然后覆盖它们" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:453 +#: ../en/ch11-mq.xml:454 msgid "" "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 " @@ -14057,7 +13142,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:465 +#: ../en/ch11-mq.xml:466 msgid "" "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 " @@ -14071,12 +13156,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:480 +#: ../en/ch11-mq.xml:481 msgid "Working on several patches at once" msgstr "同时处理多个补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:482 +#: ../en/ch11-mq.xml:483 msgid "" "The <command role=\"hg-ext-mq\">qrefresh</command> command always refreshes " "the <emphasis>topmost</emphasis> applied patch. This means that you can " @@ -14085,7 +13170,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:489 +#: ../en/ch11-mq.xml:490 msgid "" "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 " @@ -14101,12 +13186,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:506 +#: ../en/ch11-mq.xml:507 msgid "More about patches" msgstr "关于补丁的更多信息" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:508 +#: ../en/ch11-mq.xml:509 msgid "" "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> " @@ -14114,12 +13199,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:514 +#: ../en/ch11-mq.xml:515 msgid "The strip count" msgstr "修剪计数" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:516 +#: ../en/ch11-mq.xml:517 msgid "" "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 " @@ -14129,7 +13214,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:523 +#: ../en/ch11-mq.xml:524 msgid "" "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 " @@ -14143,7 +13228,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:536 +#: ../en/ch11-mq.xml:537 msgid "" "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 " @@ -14154,7 +13239,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:544 +#: ../en/ch11-mq.xml:545 msgid "" "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 " @@ -14168,7 +13253,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:557 +#: ../en/ch11-mq.xml:558 msgid "" "The <quote>standard</quote> strip count for patches is one; almost all " "patches contain one leading path name component that needs to be stripped. " @@ -14178,7 +13263,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:565 +#: ../en/ch11-mq.xml:566 msgid "" "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 " @@ -14195,12 +13280,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:584 +#: ../en/ch11-mq.xml:585 msgid "Strategies for applying a patch" msgstr "应用补丁的策略" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:586 +#: ../en/ch11-mq.xml:587 msgid "" "When <command>patch</command> applies a hunk, it tries a handful of " "successively less accurate strategies to try to make the hunk apply. This " @@ -14210,7 +13295,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:593 +#: ../en/ch11-mq.xml:594 msgid "" "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 " @@ -14221,7 +13306,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:602 +#: ../en/ch11-mq.xml:603 msgid "" "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-" @@ -14232,7 +13317,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:611 +#: ../en/ch11-mq.xml:612 msgid "" "When neither of these techniques works, <command>patch</command> prints a " "message saying that the hunk in question was rejected. It saves rejected " @@ -14249,33 +13334,33 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:629 +#: ../en/ch11-mq.xml:630 msgid "Some quirks of patch representation" msgstr "补丁的一些特性" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:631 +#: ../en/ch11-mq.xml:632 msgid "" "There are a few useful things to know about how <command>patch</command> " "works with files." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:634 +#: ../en/ch11-mq.xml:635 msgid "" "This should already be obvious, but <command>patch</command> cannot handle " "binary files." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:638 +#: ../en/ch11-mq.xml:639 msgid "" "Neither does it care about the executable bit; it creates new files as " "readable, but not executable." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:642 +#: ../en/ch11-mq.xml:643 msgid "" "<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 " @@ -14284,7 +13369,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:648 +#: ../en/ch11-mq.xml:649 msgid "" "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</" @@ -14292,7 +13377,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:654 +#: ../en/ch11-mq.xml:655 msgid "" "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 " @@ -14301,7 +13386,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:660 +#: ../en/ch11-mq.xml:661 msgid "" "<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</" @@ -14309,12 +13394,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:667 +#: ../en/ch11-mq.xml:668 msgid "Beware the fuzz" msgstr "当心毛刺" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:669 +#: ../en/ch11-mq.xml:670 msgid "" "While applying a hunk at an offset, or with a fuzz factor, will often be " "completely successful, these inexact techniques naturally leave open the " @@ -14326,7 +13411,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:679 +#: ../en/ch11-mq.xml:680 msgid "" "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 " @@ -14339,12 +13424,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:692 +#: ../en/ch11-mq.xml:693 msgid "Handling rejection" msgstr "处理拒绝" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:694 +#: ../en/ch11-mq.xml:695 msgid "" "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" @@ -14353,7 +13438,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:700 +#: ../en/ch11-mq.xml:701 msgid "" "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, " @@ -14362,7 +13447,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:706 +#: ../en/ch11-mq.xml:707 msgid "" "Unfortunately, there aren't any great techniques for dealing with rejected " "hunks. Most often, you'll need to view the <filename role=\"special\">.rej</" @@ -14370,7 +13455,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:711 +#: ../en/ch11-mq.xml:712 msgid "" "If you're feeling adventurous, Neil Brown, a Linux kernel hacker, wrote a " "tool called <command>wiggle</command> <citation>web:wiggle</citation>, which " @@ -14379,7 +13464,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:717 +#: ../en/ch11-mq.xml:718 msgid "" "Another Linux kernel hacker, Chris Mason (the author of Mercurial Queues), " "wrote a similar tool called <command>mpatch</command> <citation>web:mpatch</" @@ -14389,31 +13474,31 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:726 +#: ../en/ch11-mq.xml:727 msgid "The context in the middle of a hunk has changed." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:729 +#: ../en/ch11-mq.xml:730 msgid "A hunk is missing some context at the beginning or end." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:732 +#: ../en/ch11-mq.xml:733 msgid "" "A large hunk might apply better&emdash;either entirely or in part&emdash;if " "it was broken up into smaller hunks." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:736 +#: ../en/ch11-mq.xml:737 msgid "" "A hunk removes lines with slightly different content than those currently " "present in the file." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:740 +#: ../en/ch11-mq.xml:741 msgid "" "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, " @@ -14423,12 +13508,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:752 +#: ../en/ch11-mq.xml:753 msgid "Getting the best performance out of MQ" msgstr "MQ 的性能" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:754 +#: ../en/ch11-mq.xml:755 msgid "" "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 " @@ -14439,7 +13524,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:763 +#: ../en/ch11-mq.xml:764 msgid "" "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 " @@ -14451,14 +13536,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:774 +#: ../en/ch11-mq.xml:775 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:778 +#: ../en/ch11-mq.xml:779 msgid "" "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-" @@ -14470,7 +13555,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:789 +#: ../en/ch11-mq.xml:790 msgid "" "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 " @@ -14482,7 +13567,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:799 +#: ../en/ch11-mq.xml:800 msgid "" "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; " @@ -14490,12 +13575,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:806 +#: ../en/ch11-mq.xml:807 msgid "Updating your patches when the underlying code changes" msgstr "当基础代码改变时,更新补丁的方法" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:809 +#: ../en/ch11-mq.xml:810 msgid "" "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 " @@ -14506,7 +13591,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:818 +#: ../en/ch11-mq.xml:819 msgid "" "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 " @@ -14520,7 +13605,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:830 +#: ../en/ch11-mq.xml:831 msgid "" "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 " @@ -14530,7 +13615,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:837 +#: ../en/ch11-mq.xml:838 msgid "" "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 " @@ -14539,19 +13624,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:843 +#: ../en/ch11-mq.xml:844 msgid "The process is a little involved." msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch12-mq.xml:845 +#: ../en/ch11-mq.xml:846 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch12-mq.xml:849 +#: ../en/ch11-mq.xml:850 msgid "" "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=" @@ -14566,7 +13651,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch12-mq.xml:863 +#: ../en/ch11-mq.xml:864 msgid "" "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</" @@ -14574,7 +13659,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch12-mq.xml:868 +#: ../en/ch11-mq.xml:869 msgid "" "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 " @@ -14582,17 +13667,16 @@ msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch12-mq.xml:873 -msgid "" -"Merge all patches using \\hgcmdargs{qpush}{<option role=\"hg-ext-mq-cmd-qpush-" -"opt\">hg -m</option> <option role=\"hg-ext-mq-cmd-qpush-opt\">hg -a</" -"option>}. The <option role=\"hg-ext-mq-cmd-qpush-opt\">hg -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." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:883 +#: ../en/ch11-mq.xml:874 +msgid "" +"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." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:882 msgid "" "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=" @@ -14604,14 +13688,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:893 +#: ../en/ch11-mq.xml:892 msgid "" "When you finish resolving the effects of a patch, MQ refreshes your patch " "based on the result of the merge." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:896 +#: ../en/ch11-mq.xml:895 msgid "" "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=" @@ -14623,12 +13707,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:908 +#: ../en/ch11-mq.xml:907 msgid "Identifying patches" msgstr "标识补丁" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:910 +#: ../en/ch11-mq.xml:909 msgid "" "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 " @@ -14638,7 +13722,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:917 +#: ../en/ch11-mq.xml:916 msgid "" "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 " @@ -14647,7 +13731,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:923 +#: ../en/ch11-mq.xml:922 msgid "" "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 " @@ -14656,51 +13740,45 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:929 +#: ../en/ch11-mq.xml:928 msgid "" "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." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:941 +"special tags <literal role=\"tag\">qbase</literal> and <literal role=\"tag" +"\">qtip</literal> identify the <quote>bottom-most</quote> and topmost applied " +"patches, respectively." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:938 msgid "" "These additions to Mercurial's normal tagging capabilities make dealing with " "patches even more of a breeze." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:944 +#: ../en/ch11-mq.xml:941 msgid "Want to patchbomb a mailing list with your latest series of changes?" msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:948 +#: ../en/ch11-mq.xml:944 msgid "" "(Don't know what <quote>patchbombing</quote> is? See section <xref linkend=" "\"sec.hgext.patchbomb\"/>.)" msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:951 +#: ../en/ch11-mq.xml:947 msgid "" "Need to see all of the patches since <literal>foo.patch</literal> that have " "touched files in a subdirectory of your tree?" msgstr "" -#. type: Content of: <book><chapter><sect1><itemizedlist><listitem><programlisting><emphasis> -#: ../en/ch12-mq.xml:955 -msgid "subdir" -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:960 +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch11-mq.xml:954 msgid "" "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 " @@ -14708,7 +13786,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:965 +#: ../en/ch11-mq.xml:959 msgid "" "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 " @@ -14719,19 +13797,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:978 +#: ../en/ch11-mq.xml:972 msgid "Useful things to know about" msgstr "其它需要了解的东西" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:980 +#: ../en/ch11-mq.xml:974 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:985 +#: ../en/ch11-mq.xml:979 msgid "" "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 " @@ -14742,7 +13820,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:994 +#: ../en/ch11-mq.xml:988 msgid "" "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 " @@ -14752,12 +13830,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:1005 +#: ../en/ch11-mq.xml:999 msgid "Managing patches in a repository" msgstr "在版本库管理补丁" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1007 +#: ../en/ch11-mq.xml:1001 msgid "" "Because MQ's <filename role=\"special\" class=\"directory\">.hg/patches</" "filename> directory resides outside a Mercurial repository's working " @@ -14766,7 +13844,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1013 +#: ../en/ch11-mq.xml:1007 msgid "" "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 " @@ -14777,7 +13855,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1022 +#: ../en/ch11-mq.xml:1016 msgid "" "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 " @@ -14789,7 +13867,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1032 +#: ../en/ch11-mq.xml:1026 msgid "" "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 " @@ -14797,12 +13875,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:1038 +#: ../en/ch11-mq.xml:1032 msgid "MQ support for patch repositories" msgstr "MQ 支持补丁版本库" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1040 +#: ../en/ch11-mq.xml:1034 msgid "" "MQ helps you to work with the <filename role=\"special\" class=\"directory\">." "hg/patches</filename> directory as a repository; when you prepare a " @@ -14813,7 +13891,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch12-mq.xml:1050 +#: ../en/ch11-mq.xml:1044 msgid "" "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=" @@ -14824,7 +13902,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch12-mq.xml:1059 +#: ../en/ch11-mq.xml:1053 msgid "" "(<command role=\"hg-cmd\">hg qinit <option role=\"hg-ext-mq-cmd-qinit-opt" "\">hg -c</option></command> does this for you automatically); you " @@ -14833,7 +13911,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1066 +#: ../en/ch11-mq.xml:1060 msgid "" "As a convenience, if MQ notices that the <filename class=\"directory\">.hg/" "patches</filename> directory is a repository, it will automatically <command " @@ -14841,7 +13919,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1071 +#: ../en/ch11-mq.xml:1065 msgid "" "MQ provides a shortcut command, <command role=\"hg-ext-mq\">qcommit</" "command>, that runs <command role=\"hg-cmd\">hg commit</command> in the " @@ -14850,7 +13928,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1077 +#: ../en/ch11-mq.xml:1071 msgid "" "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 " @@ -14859,26 +13937,26 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1087 +#: ../en/ch11-mq.xml:1080 msgid "" "You can then issue commands of the form <command>mq pull</command> from the " "main repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:1092 +#: ../en/ch11-mq.xml:1085 msgid "A few things to watch out for" msgstr "需要注意的事情" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1094 +#: ../en/ch11-mq.xml:1087 msgid "" "MQ's support for working with a repository full of patches is limited in a " "few small respects." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1097 +#: ../en/ch11-mq.xml:1090 msgid "" "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 " @@ -14892,12 +13970,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:1113 +#: ../en/ch11-mq.xml:1106 msgid "Third party tools for working with patches" msgstr "操作补丁的第三方工具" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1115 +#: ../en/ch11-mq.xml:1108 msgid "" "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 " @@ -14905,7 +13983,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1119 +#: ../en/ch11-mq.xml:1112 msgid "" "The <command>diffstat</command> command <citation>web:diffstat</citation> " "generates a histogram of the modifications made to each file in a patch. It " @@ -14918,7 +13996,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1133 +#: ../en/ch11-mq.xml:1126 msgid "" "The <literal role=\"package\">patchutils</literal> package <citation>web:" "patchutils</citation> is invaluable. It provides a set of small utilities " @@ -14933,12 +14011,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:1149 +#: ../en/ch11-mq.xml:1142 msgid "Good ways to work with patches" msgstr "操作补丁的好习惯" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1151 +#: ../en/ch11-mq.xml:1144 msgid "" "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 " @@ -14947,7 +14025,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1157 +#: ../en/ch11-mq.xml:1150 msgid "" "Give your patches descriptive names. A good name for a patch might be " "<filename>rework-device-alloc.patch</filename>, because it will immediately " @@ -14961,7 +14039,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1169 +#: ../en/ch11-mq.xml:1162 msgid "" "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 " @@ -14974,7 +14052,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1179 +#: ../en/ch11-mq.xml:1172 msgid "" "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=" @@ -14985,17 +14063,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:1190 +#: ../en/ch11-mq.xml:1183 msgid "MQ cookbook" msgstr "MQ 手册" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:1193 +#: ../en/ch11-mq.xml:1186 msgid "Manage <quote>trivial</quote> patches" msgstr "管理<quote>琐碎的</quote>补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1195 +#: ../en/ch11-mq.xml:1188 msgid "" "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 " @@ -15004,26 +14082,26 @@ # #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1200 +#: ../en/ch11-mq.xml:1193 msgid "" "Begin by downloading and unpacking the source tarball, and turning it into a " "Mercurial repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1205 +#: ../en/ch11-mq.xml:1198 msgid "Continue by creating a patch stack and making your changes." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1210 +#: ../en/ch11-mq.xml:1203 msgid "" "Let's say a few weeks or months pass, and your package author releases a new " "version. First, bring their changes into the repository." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1216 +#: ../en/ch11-mq.xml:1209 msgid "" "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" @@ -15033,17 +14111,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1224 +#: ../en/ch11-mq.xml:1217 msgid "Finally, you can apply your patches on top of the new tree." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:1231 +#: ../en/ch11-mq.xml:1224 msgid "Combining entire patches" msgstr "组合全部的补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1233 +#: ../en/ch11-mq.xml:1226 msgid "" "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, " @@ -15053,7 +14131,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1241 +#: ../en/ch11-mq.xml:1234 msgid "" "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</" @@ -15064,19 +14142,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch12-mq.xml:1252 +#: ../en/ch11-mq.xml:1245 msgid "Merging part of one patch into another" msgstr "合并补丁的部分内容到其它补丁" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1254 +#: ../en/ch11-mq.xml:1247 msgid "" "Merging <emphasis>part</emphasis> of one patch into another is more difficult " "than combining entire patches." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1258 +#: ../en/ch11-mq.xml:1251 msgid "" "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=" @@ -15090,7 +14168,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1271 +#: ../en/ch11-mq.xml:1264 msgid "" "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 " @@ -15099,32 +14177,32 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1279 +#: ../en/ch11-mq.xml:1272 msgid "This command prints three different kinds of number:" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:1282 +#: ../en/ch11-mq.xml:1275 msgid "" "(in the first column) a <emphasis>file number</emphasis> to identify each " "file modified in the patch;" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:1286 +#: ../en/ch11-mq.xml:1279 msgid "" "(on the next line, indented) the line number within a modified file where a " "hunk starts; and" msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch12-mq.xml:1289 +#: ../en/ch11-mq.xml:1282 msgid "" "(on the same line) a <emphasis>hunk number</emphasis> to identify that hunk." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1293 +#: ../en/ch11-mq.xml:1286 msgid "" "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 " @@ -15134,7 +14212,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch12-mq.xml:1301 +#: ../en/ch11-mq.xml:1294 msgid "" "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=" @@ -15142,19 +14220,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch12-mq.xml:1308 +#: ../en/ch11-mq.xml:1301 msgid "Differences between quilt and MQ" msgstr "MQ 与 quilt 的区别" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1310 +#: ../en/ch11-mq.xml:1303 msgid "" "If you are already familiar with quilt, MQ provides a similar command set. " "There are a few differences in the way that it works." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch12-mq.xml:1314 +#: ../en/ch11-mq.xml:1307 msgid "" "You will already have noticed that most quilt commands have MQ counterparts " "that simply begin with a <quote><literal>q</literal></quote>. The exceptions " @@ -15166,12 +14244,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch13-mq-collab.xml:5 +#: ../en/ch12-mq-collab.xml:5 msgid "Advanced uses of Mercurial Queues" msgstr "MQ 的高级用法" #. type: Content of: <book><chapter><para> -#: ../en/ch13-mq-collab.xml:7 +#: ../en/ch12-mq-collab.xml:7 msgid "" "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 " @@ -15179,7 +14257,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch13-mq-collab.xml:12 +#: ../en/ch12-mq-collab.xml:12 msgid "" "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 " @@ -15189,7 +14267,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch13-mq-collab.xml:18 +#: ../en/ch12-mq-collab.xml:18 msgid "" "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 " @@ -15197,12 +14275,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:24 +#: ../en/ch12-mq-collab.xml:24 msgid "The problem of many targets" msgstr "多个目标的问题" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:26 +#: ../en/ch12-mq-collab.xml:26 msgid "" "The Linux kernel changes rapidly, and has never been internally stable; " "developers frequently make drastic changes between releases. This means that " @@ -15212,14 +14290,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:33 +#: ../en/ch12-mq-collab.xml:33 msgid "" "To maintain a driver, we have to keep a number of distinct versions of Linux " "in mind." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:36 +#: ../en/ch12-mq-collab.xml:36 msgid "" "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, " @@ -15228,7 +14306,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:42 +#: ../en/ch12-mq-collab.xml:42 msgid "" "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 " @@ -15239,7 +14317,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:50 +#: ../en/ch12-mq-collab.xml:50 msgid "" "Finally, we make software releases on a schedule that is necessarily not " "aligned with those used by Linux distributors and kernel developers, so that " @@ -15248,19 +14326,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch13-mq-collab.xml:58 +#: ../en/ch12-mq-collab.xml:58 msgid "Tempting approaches that don't work well" msgstr "工作不好的诱人方法" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:60 +#: ../en/ch12-mq-collab.xml:60 msgid "" "There are two <quote>standard</quote> ways to maintain a piece of software " "that has to target many different environments." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:64 +#: ../en/ch12-mq-collab.xml:64 msgid "" "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 " @@ -15272,7 +14350,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:74 +#: ../en/ch12-mq-collab.xml:74 msgid "" "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 " @@ -15284,7 +14362,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:83 +#: ../en/ch12-mq-collab.xml:83 msgid "" "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 " @@ -15296,14 +14374,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:93 +#: ../en/ch12-mq-collab.xml:93 msgid "" "These approaches have the added weakness of making it difficult to generate " "well-formed patches to submit upstream." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:97 +#: ../en/ch12-mq-collab.xml:97 msgid "" "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 " @@ -15311,12 +14389,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:105 +#: ../en/ch12-mq-collab.xml:105 msgid "Conditionally applying patches with guards" msgstr "有条件的应用补丁" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:107 +#: ../en/ch12-mq-collab.xml:107 msgid "" "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 " @@ -15326,14 +14404,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:116 +#: ../en/ch12-mq-collab.xml:116 msgid "" "This gives us a tiny repository that contains two patches that don't have any " "dependencies on each other, because they touch different files." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:120 +#: ../en/ch12-mq-collab.xml:120 msgid "" "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 " @@ -15343,7 +14421,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:127 +#: ../en/ch12-mq-collab.xml:127 msgid "" "A patch can have an arbitrary number of guards; each one is " "<emphasis>positive</emphasis> (<quote>apply this patch if this guard is " @@ -15352,12 +14430,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:135 +#: ../en/ch12-mq-collab.xml:135 msgid "Controlling the guards on a patch" msgstr "控制补丁的应用条件" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:137 +#: ../en/ch12-mq-collab.xml:137 msgid "" "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 " @@ -15366,21 +14444,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:144 +#: ../en/ch12-mq-collab.xml:144 msgid "" "To set a positive guard on a patch, prefix the name of the guard with a " "<quote><literal>+</literal></quote>." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:149 +#: ../en/ch12-mq-collab.xml:149 msgid "" "To set a negative guard on a patch, prefix the name of the guard with a " "<quote><literal>-</literal></quote>." msgstr "" #. type: Content of: <book><chapter><sect1><note><para> -#: ../en/ch13-mq-collab.xml:156 +#: ../en/ch12-mq-collab.xml:156 msgid "" "The <command role=\"hg-ext-mq\">qguard</command> command <emphasis>sets</" "emphasis> the guards on a patch; it doesn't <emphasis>modify</emphasis> " @@ -15392,7 +14470,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:165 +#: ../en/ch12-mq-collab.xml:165 msgid "" "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 " @@ -15402,12 +14480,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:177 +#: ../en/ch12-mq-collab.xml:177 msgid "Selecting the guards to use" msgstr "选择使用的条件" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:179 +#: ../en/ch12-mq-collab.xml:179 msgid "" "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 " @@ -15417,7 +14495,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:186 +#: ../en/ch12-mq-collab.xml:186 msgid "" "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 " @@ -15425,21 +14503,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:193 +#: ../en/ch12-mq-collab.xml:193 msgid "" "In case you're interested, the currently selected guards are stored in the " "<filename role=\"special\">guards</filename> file." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:198 +#: ../en/ch12-mq-collab.xml:198 msgid "" "We can see the effect the selected guards have when we run <command role=\"hg-" "ext-mq\">qpush</command>." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:203 +#: ../en/ch12-mq-collab.xml:203 msgid "" "A guard cannot start with a <quote><literal>+</literal></quote> or " "<quote><literal>-</literal></quote> character. The name of a guard must not " @@ -15448,61 +14526,61 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:212 +#: ../en/ch12-mq-collab.xml:212 msgid "Changing the selected guards changes the patches that are applied." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:217 +#: ../en/ch12-mq-collab.xml:217 msgid "" "You can see in the example below that negative guards take precedence over " "positive guards." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:224 +#: ../en/ch12-mq-collab.xml:224 msgid "MQ's rules for applying patches" msgstr "MQ 应用补丁的规则" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:226 +#: ../en/ch12-mq-collab.xml:226 msgid "" "The rules that MQ uses when deciding whether to apply a patch are as follows." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:229 +#: ../en/ch12-mq-collab.xml:229 msgid "A patch that has no guards is always applied." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:232 +#: ../en/ch12-mq-collab.xml:232 msgid "" "If the patch has any negative guard that matches any currently selected " "guard, the patch is skipped." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:235 +#: ../en/ch12-mq-collab.xml:235 msgid "" "If the patch has any positive guard that matches any currently selected " "guard, the patch is applied." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:238 +#: ../en/ch12-mq-collab.xml:238 msgid "" "If the patch has positive or negative guards, but none matches any currently " "selected guard, the patch is skipped." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:245 +#: ../en/ch12-mq-collab.xml:245 msgid "Trimming the work environment" msgstr "修剪工作环境" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:247 +#: ../en/ch12-mq-collab.xml:247 msgid "" "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 " @@ -15512,7 +14590,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:254 +#: ../en/ch12-mq-collab.xml:254 msgid "" "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 " @@ -15524,7 +14602,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:263 +#: ../en/ch12-mq-collab.xml:263 msgid "" "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 " @@ -15532,12 +14610,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:270 +#: ../en/ch12-mq-collab.xml:270 msgid "Dividing up the <filename role=\"special\">series</filename> file" msgstr "分类补丁<filename role=\"special\">系列</filename>" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:273 +#: ../en/ch12-mq-collab.xml:273 msgid "" "I categorise the patches in the <filename role=\"special\">series</filename> " "file into a number of logical groups. Each section of like patches begins " @@ -15546,14 +14624,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:279 +#: ../en/ch12-mq-collab.xml:279 msgid "" "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." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:283 +#: ../en/ch12-mq-collab.xml:283 msgid "" "The <quote>accepted</quote> group. Patches that the development team has " "submitted to the maintainer of the Infiniband subsystem, and which he has " @@ -15564,14 +14642,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:291 +#: ../en/ch12-mq-collab.xml:291 msgid "" "The <quote>rework</quote> group. Patches that I have submitted, but that the " "upstream maintainer has requested modifications to before he will accept them." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:296 +#: ../en/ch12-mq-collab.xml:296 msgid "" "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 " @@ -15582,21 +14660,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:305 +#: ../en/ch12-mq-collab.xml:305 msgid "" "The <quote>in progress</quote> group. Patches that are actively being " "developed, and should not be submitted anywhere yet." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:309 +#: ../en/ch12-mq-collab.xml:309 msgid "" "The <quote>backport</quote> group. Patches that adapt the source tree to " "older versions of the kernel tree." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:313 +#: ../en/ch12-mq-collab.xml:313 msgid "" "The <quote>do not ship</quote> group. Patches that for some reason should " "never be submitted upstream. For example, one such patch might change " @@ -15606,7 +14684,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:321 +#: ../en/ch12-mq-collab.xml:321 msgid "" "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 " @@ -15616,7 +14694,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:329 +#: ../en/ch12-mq-collab.xml:329 msgid "" "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 " @@ -15624,7 +14702,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:334 +#: ../en/ch12-mq-collab.xml:334 msgid "" "The <quote>backport</quote> and <quote>do not ship</quote> patches float at " "the end of the <filename role=\"special\">series</filename> file. The " @@ -15633,19 +14711,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:343 +#: ../en/ch12-mq-collab.xml:343 msgid "Maintaining the patch series" msgstr "维护补丁系列" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:345 +#: ../en/ch12-mq-collab.xml:345 msgid "" "In my work, I use a number of guards to control which patches are to be " "applied." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:349 +#: ../en/ch12-mq-collab.xml:349 msgid "" "<quote>Accepted</quote> patches are guarded with <literal>accepted</" "literal>. I enable this guard most of the time. When I'm applying the " @@ -15654,7 +14732,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:356 +#: ../en/ch12-mq-collab.xml:356 msgid "" "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 " @@ -15662,21 +14740,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:362 +#: ../en/ch12-mq-collab.xml:362 msgid "" "Those patches that need reworking before being resubmitted are guarded with " "<literal>rework</literal>." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:366 +#: ../en/ch12-mq-collab.xml:366 msgid "" "For those patches that are still under development, I use <literal>devel</" "literal>." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch13-mq-collab.xml:369 +#: ../en/ch12-mq-collab.xml:369 msgid "" "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 " @@ -15684,7 +14762,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch13-mq-collab.xml:374 +#: ../en/ch12-mq-collab.xml:374 msgid "" "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 " @@ -15693,12 +14771,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch13-mq-collab.xml:381 +#: ../en/ch12-mq-collab.xml:381 msgid "The art of writing backport patches" msgstr "编写向后移植补丁的艺术" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:383 +#: ../en/ch12-mq-collab.xml:383 msgid "" "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 " @@ -15707,7 +14785,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:389 +#: ../en/ch12-mq-collab.xml:389 msgid "" "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 " @@ -15721,7 +14799,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:402 +#: ../en/ch12-mq-collab.xml:402 msgid "" "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 " @@ -15734,17 +14812,17 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch13-mq-collab.xml:417 +#: ../en/ch12-mq-collab.xml:417 msgid "Useful tips for developing with MQ" msgstr "使用 MQ 开发的技巧" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch13-mq-collab.xml:420 +#: ../en/ch12-mq-collab.xml:420 msgid "Organising patches in directories" msgstr "将补丁放到几个目录中" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:422 +#: ../en/ch12-mq-collab.xml:422 msgid "" "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 " @@ -15752,7 +14830,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:427 +#: ../en/ch12-mq-collab.xml:427 msgid "" "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 " @@ -15760,12 +14838,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch13-mq-collab.xml:434 +#: ../en/ch12-mq-collab.xml:434 msgid "Viewing the history of a patch" msgstr "察看补丁的历史" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:436 +#: ../en/ch12-mq-collab.xml:436 msgid "" "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." @@ -15778,7 +14856,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:448 +#: ../en/ch12-mq-collab.xml:448 msgid "" "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 " @@ -15791,18 +14869,18 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:459 +#: ../en/ch12-mq-collab.xml:459 msgid "" "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</" +"\">extensions</literal> section of your <filename role=\"special\">~/.hgrc</" "filename>." msgstr "" # #. &example.hg-interdiff; #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:465 +#: ../en/ch12-mq-collab.xml:466 msgid "" "The <command>interdiff</command> command expects to be passed the names of " "two files, but the <literal role=\"hg-ext\">extdiff</literal> extension " @@ -15815,7 +14893,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:477 +#: ../en/ch12-mq-collab.xml:478 msgid "" "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 " @@ -15823,16 +14901,16 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:482 +#: ../en/ch12-mq-collab.xml:482 msgid "" "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</" +"Mercurial command, again by editing your <filename role=\"special\">~/.hgrc</" "filename>." msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:489 +#: ../en/ch12-mq-collab.xml:489 msgid "" "This directs <literal role=\"hg-ext\">hgext</literal> to make an " "<literal>interdiff</literal> command available, so you can now shorten the " @@ -15841,7 +14919,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><note><para> -#: ../en/ch13-mq-collab.xml:498 +#: ../en/ch12-mq-collab.xml:497 msgid "" "The <command>interdiff</command> command works well only if the underlying " "files against which versions of a patch are generated remain the same. If " @@ -15850,7 +14928,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch13-mq-collab.xml:506 +#: ../en/ch12-mq-collab.xml:505 msgid "" "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, " @@ -15858,12 +14936,12 @@ msgstr "" #. type: Content of: <book><chapter><title> -#: ../en/ch14-hgext.xml:5 +#: ../en/ch13-hgext.xml:5 msgid "Adding functionality with extensions" msgstr "使用扩展增加功能" #. type: Content of: <book><chapter><para> -#: ../en/ch14-hgext.xml:7 +#: ../en/ch13-hgext.xml:7 msgid "" "While the core of Mercurial is quite complete from a functionality " "standpoint, it's deliberately shorn of fancy features. This approach of " @@ -15872,7 +14950,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch14-hgext.xml:12 +#: ../en/ch13-hgext.xml:12 msgid "" "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 " @@ -15881,7 +14959,7 @@ msgstr "" #. type: Content of: <book><chapter><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:18 +#: ../en/ch13-hgext.xml:18 msgid "" "Section <xref linkend=\"sec.tour-merge.fetch\"/> covers the <literal role=" "\"hg-ext\">fetch</literal> extension; this combines pulling new changes and " @@ -15890,7 +14968,7 @@ msgstr "" #. type: Content of: <book><chapter><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:24 +#: ../en/ch13-hgext.xml:24 msgid "" "In chapter <xref linkend=\"chap.hook\"/>, we covered several extensions that " "are useful for hook-related functionality: <literal role=\"hg-ext\">acl</" @@ -15900,7 +14978,7 @@ msgstr "" #. type: Content of: <book><chapter><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:33 +#: ../en/ch13-hgext.xml:33 msgid "" "The Mercurial Queues patch management extension is so invaluable that it " "merits two chapters and an appendix all to itself. Chapter <xref linkend=" @@ -15910,7 +14988,7 @@ msgstr "" #. type: Content of: <book><chapter><para> -#: ../en/ch14-hgext.xml:43 +#: ../en/ch13-hgext.xml:43 msgid "" "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 " @@ -15918,7 +14996,7 @@ msgstr "" #. type: Content of: <book><chapter><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:48 +#: ../en/ch13-hgext.xml:48 msgid "" "In section <xref linkend=\"sec.hgext.inotify\"/>, we'll discuss the " "possibility of <emphasis>huge</emphasis> performance improvements using the " @@ -15926,21 +15004,21 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch14-hgext.xml:55 +#: ../en/ch13-hgext.xml:55 msgid "" "Improve performance with the <literal role=\"hg-ext\">inotify</literal> " "extension" msgstr "使用扩展 <literal role=\"hg-ext\">inotify</literal> 以提高性能" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:58 +#: ../en/ch13-hgext.xml:58 msgid "" "Are you interested in having some of the most common Mercurial operations run " "as much as a hundred times faster? Read on!" msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:62 +#: ../en/ch13-hgext.xml:62 msgid "" "Mercurial has great performance under normal circumstances. For example, " "when you run the <command role=\"hg-cmd\">hg status</command> command, " @@ -15952,7 +15030,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:72 +#: ../en/ch13-hgext.xml:72 msgid "" "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, " @@ -15964,7 +15042,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:82 +#: ../en/ch13-hgext.xml:82 msgid "" "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 " @@ -15973,7 +15051,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:88 +#: ../en/ch13-hgext.xml:88 msgid "" "Many modern operating systems contain a file notification facility. If a " "program signs up to an appropriate service, the operating system will notify " @@ -15983,7 +15061,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:95 +#: ../en/ch13-hgext.xml:95 msgid "" "Mercurial's <literal role=\"hg-ext\">inotify</literal> extension talks to the " "kernel's <literal>inotify</literal> component to optimise <command role=\"hg-" @@ -15998,7 +15076,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:108 +#: ../en/ch13-hgext.xml:108 msgid "" "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. " @@ -16008,12 +15086,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:115 +#: ../en/ch13-hgext.xml:115 msgid "Before we continue, please pay attention to some caveats." msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:118 +#: ../en/ch13-hgext.xml:118 msgid "" "The <literal role=\"hg-ext\">inotify</literal> extension is Linux-specific. " "Because it interfaces directly to the Linux kernel's <literal>inotify</" @@ -16021,7 +15099,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:123 +#: ../en/ch13-hgext.xml:123 msgid "" "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</" @@ -16030,7 +15108,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:130 +#: ../en/ch13-hgext.xml:130 msgid "" "Not all filesystems are suitable for use with the <literal role=\"hg-ext" "\">inotify</literal> extension. Network filesystems such as NFS are a non-" @@ -16042,7 +15120,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:141 +#: ../en/ch13-hgext.xml:141 msgid "" "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 " @@ -16050,7 +15128,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:146 +#: ../en/ch13-hgext.xml:146 msgid "" "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</" @@ -16058,7 +15136,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><note><para> -#: ../en/ch14-hgext.xml:150 +#: ../en/ch13-hgext.xml:150 msgid "" "There are <emphasis>two</emphasis> Python <literal>inotify</literal> binding " "libraries. One of them is called <literal>pyinotify</literal>, and is " @@ -16068,14 +15146,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:157 +#: ../en/ch13-hgext.xml:157 msgid "" "To get going, it's best to already have a functioning copy of Mercurial " "installed." msgstr "" #. type: Content of: <book><chapter><sect1><note><para> -#: ../en/ch14-hgext.xml:160 +#: ../en/ch13-hgext.xml:160 msgid "" "If you follow the instructions below, you'll be <emphasis>replacing</" "emphasis> and overwriting any existing installation of Mercurial that you " @@ -16084,14 +15162,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch14-hgext.xml:167 +#: ../en/ch13-hgext.xml:167 msgid "" "Clone the Python <literal>inotify</literal> binding repository. Build and " "install it." msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch14-hgext.xml:175 +#: ../en/ch13-hgext.xml:174 msgid "" "Clone the <filename class=\"directory\">crew</filename> Mercurial " "repository. Clone the <literal role=\"hg-ext\">inotify</literal> patch " @@ -16100,7 +15178,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch14-hgext.xml:188 +#: ../en/ch13-hgext.xml:184 msgid "" "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 " @@ -16108,7 +15186,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch14-hgext.xml:194 +#: ../en/ch13-hgext.xml:190 msgid "" "Go into the <filename class=\"directory\">inotify</filename> repo, and apply " "all of the <literal role=\"hg-ext\">inotify</literal> patches using the " @@ -16117,27 +15195,27 @@ msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch14-hgext.xml:204 +#: ../en/ch13-hgext.xml:199 msgid "" "If you get an error message from <command role=\"hg-ext-mq\">qpush</command>, " "you should not continue. Instead, ask for help." msgstr "" #. type: Content of: <book><chapter><sect1><orderedlist><listitem><para> -#: ../en/ch14-hgext.xml:208 +#: ../en/ch13-hgext.xml:203 msgid "Build and install the patched version of Mercurial." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:216 +#: ../en/ch13-hgext.xml:209 msgid "" "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>." -msgstr "" - -#. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:221 +"entry to your <filename role=\"special\">~/.hgrc</filename>." +msgstr "" + +#. type: Content of: <book><chapter><sect1><para> +#: ../en/ch13-hgext.xml:214 msgid "" "When the <literal role=\"hg-ext\">inotify</literal> extension is enabled, " "Mercurial will automatically and transparently start the status daemon the " @@ -16146,7 +15224,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:227 +#: ../en/ch13-hgext.xml:220 msgid "" "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=" @@ -16156,7 +15234,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:235 +#: ../en/ch13-hgext.xml:228 msgid "" "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 " @@ -16172,7 +15250,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:249 +#: ../en/ch13-hgext.xml:242 msgid "" "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 " @@ -16182,7 +15260,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:256 +#: ../en/ch13-hgext.xml:249 msgid "" "When you're using the <literal role=\"hg-ext\">inotify</literal> extension, " "you should notice <emphasis>no difference at all</emphasis> in Mercurial's " @@ -16193,14 +15271,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch14-hgext.xml:267 +#: ../en/ch13-hgext.xml:260 msgid "" "Flexible diff support with the <literal role=\"hg-ext\">extdiff</literal> " "extension" msgstr "使用扩展 <literal role=\"hg-ext\">extdiff</literal> 以扩展差异支持" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:270 +#: ../en/ch13-hgext.xml:263 msgid "" "Mercurial's built-in <command role=\"hg-cmd\">hg diff</command> command " "outputs plaintext unified diffs." @@ -16209,7 +15287,7 @@ "不同。" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:275 +#: ../en/ch13-hgext.xml:268 msgid "" "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 " @@ -16217,16 +15295,16 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:280 +#: ../en/ch13-hgext.xml:273 msgid "" "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</" +"\">extensions</literal> section of your <filename role=\"special\">~/.hgrc</" "filename>, simply add a one-line entry to enable the extension." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:286 +#: ../en/ch13-hgext.xml:280 msgid "" "This introduces a command named <command role=\"hg-ext-extdiff\">extdiff</" "command>, which by default uses your system's <command>diff</command> command " @@ -16235,7 +15313,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:294 +#: ../en/ch13-hgext.xml:288 msgid "" "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</" @@ -16243,7 +15321,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:299 +#: ../en/ch13-hgext.xml:293 msgid "" "As the <quote><literal>making snapshot</literal></quote> lines of output " "above imply, the <command role=\"hg-ext-extdiff\">extdiff</command> command " @@ -16257,7 +15335,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:312 +#: ../en/ch13-hgext.xml:306 msgid "" "Snapshot directory names have the same base name as your repository. If your " "repository path is <filename class=\"directory\">/quux/bar/foo</filename>, " @@ -16274,7 +15352,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:328 +#: ../en/ch13-hgext.xml:322 msgid "" "The <command role=\"hg-ext-extdiff\">extdiff</command> command accepts two " "important options. The <option role=\"hg-ext-extdiff-cmd-extdiff-opt\">hg -p</" @@ -16292,7 +15370,7 @@ # #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:345 +#: ../en/ch13-hgext.xml:339 msgid "" "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-" @@ -16302,14 +15380,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:354 +#: ../en/ch13-hgext.xml:348 msgid "" "Launching a visual diff tool is just as easy. Here's how to launch the " "<command>kdiff3</command> viewer." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:358 +#: ../en/ch13-hgext.xml:352 msgid "" "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 " @@ -16319,12 +15397,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch14-hgext.xml:366 +#: ../en/ch13-hgext.xml:360 msgid "Defining command aliases" msgstr "定义命令的别名" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch14-hgext.xml:368 +#: ../en/ch13-hgext.xml:362 msgid "" "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, " @@ -16334,9 +15412,9 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch14-hgext.xml:375 -msgid "" -"All you need to do is edit your <filename role=\"special\"> /.hgrc</" +#: ../en/ch13-hgext.xml:369 +msgid "" +"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 " @@ -16346,7 +15424,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch14-hgext.xml:384 +#: ../en/ch13-hgext.xml:379 msgid "" "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 " @@ -16357,7 +15435,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch14-hgext.xml:393 +#: ../en/ch13-hgext.xml:389 msgid "" "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></" @@ -16367,19 +15445,19 @@ msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch14-hgext.xml:406 +#: ../en/ch13-hgext.xml:403 msgid "" "Cherrypicking changes with the <literal role=\"hg-ext\">transplant</literal> " "extension" msgstr "使用扩展 <literal role=\"hg-ext\">transplant</literal> 以挑选修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:409 +#: ../en/ch13-hgext.xml:406 msgid "Need to have a long chat with Brendan about this." msgstr "" #. type: Content of: <book><chapter><sect1><title> -#: ../en/ch14-hgext.xml:413 +#: ../en/ch13-hgext.xml:410 msgid "" "Send changes via email with the <literal role=\"hg-ext\">patchbomb</literal> " "extension" @@ -16387,7 +15465,7 @@ "使用扩展 <literal role=\"hg-ext\">patchbomb</literal> 通过 email 发送修改" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:416 +#: ../en/ch13-hgext.xml:413 msgid "" "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 " @@ -16397,7 +15475,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:424 +#: ../en/ch13-hgext.xml:421 msgid "" "Mercurial makes it easy to send changes over email for review or application, " "via its <literal role=\"hg-ext\">patchbomb</literal> extension. The " @@ -16408,7 +15486,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:432 +#: ../en/ch13-hgext.xml:429 msgid "" "As usual, the basic configuration of the <literal role=\"hg-ext\">patchbomb</" "literal> extension takes just one or two lines in your <filename role=" @@ -16416,14 +15494,14 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:437 +#: ../en/ch13-hgext.xml:435 msgid "" "Once you've enabled the extension, you will have a new command available, " "named <command role=\"hg-ext-patchbomb\">email</command>." msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:441 +#: ../en/ch13-hgext.xml:439 msgid "" "The safest and best way to invoke the <command role=\"hg-ext-patchbomb" "\">email</command> command is to <emphasis>always</emphasis> run it first " @@ -16436,7 +15514,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:452 +#: ../en/ch13-hgext.xml:450 msgid "" "The <command role=\"hg-ext-patchbomb\">email</command> command accepts the " "same kind of revision syntax as every other Mercurial command. For example, " @@ -16445,7 +15523,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:457 +#: ../en/ch13-hgext.xml:455 msgid "" "You can also specify a <emphasis>repository</emphasis> to compare with. If " "you provide a repository but no revisions, the <command role=\"hg-ext-" @@ -16457,7 +15535,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:466 +#: ../en/ch13-hgext.xml:464 msgid "" "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 " @@ -16468,7 +15546,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:474 +#: ../en/ch13-hgext.xml:472 msgid "" "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 " @@ -16476,7 +15554,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><para> -#: ../en/ch14-hgext.xml:479 +#: ../en/ch13-hgext.xml:477 msgid "" "If you send multiple revisions, the <command role=\"hg-ext-patchbomb\">email</" "command> command will usually send one message per changeset. It will " @@ -16485,12 +15563,12 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><title> -#: ../en/ch14-hgext.xml:486 +#: ../en/ch13-hgext.xml:484 msgid "Changing the behaviour of patchbombs" msgstr "修改 patchbomb 的行为" #. type: Content of: <book><chapter><sect1><sect2><para> -#: ../en/ch14-hgext.xml:488 +#: ../en/ch13-hgext.xml:486 msgid "" "Not every project has exactly the same conventions for sending changes in " "email; the <literal role=\"hg-ext\">patchbomb</literal> extension tries to " @@ -16498,7 +15576,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:494 +#: ../en/ch13-hgext.xml:492 msgid "" "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> " @@ -16506,7 +15584,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:500 +#: ../en/ch13-hgext.xml:498 msgid "" "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 " @@ -16514,7 +15592,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:506 +#: ../en/ch13-hgext.xml:504 msgid "" "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 " @@ -16523,7 +15601,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:514 +#: ../en/ch13-hgext.xml:512 msgid "" "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-" @@ -16531,7 +15609,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:520 +#: ../en/ch13-hgext.xml:518 msgid "" "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 " @@ -16542,7 +15620,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:530 +#: ../en/ch13-hgext.xml:528 msgid "" "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-" @@ -16551,7 +15629,7 @@ msgstr "" #. type: Content of: <book><chapter><sect1><sect2><itemizedlist><listitem><para> -#: ../en/ch14-hgext.xml:537 +#: ../en/ch13-hgext.xml:535 msgid "" "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-" @@ -16560,3 +15638,60 @@ "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." msgstr "" + +#~ msgid "Introduction" +#~ msgstr "简介" + +#~ msgid "About revision control" +#~ msgstr "关于版本控制" + +#~ msgid "Why use revision control?" +#~ msgstr "为什么使用版本控制?" + +#~ msgid "The many names of revision control" +#~ msgstr "版本控制的别名" + +#~ msgid "Revision control (RCS)" +#~ msgstr "版本控制(RCS)" + +#~ msgid "Software configuration management (SCM), or configuration management" +#~ msgstr "软件配置管理(SCM),或配置管理" + +#~ msgid "Source code management" +#~ msgstr "源代码管理" + +#~ msgid "Source code control, or source control" +#~ msgstr "源代码控制,或源控制" + +#~ msgid "Version control (VCS)" +#~ msgstr "版本控制(VCS)" + +#~ msgid "A short history of revision control" +#~ msgstr "版本控制简史" + +#~ msgid "Trends in revision control" +#~ msgstr "版本控制的发展趋势" + +#~ msgid "A few of the advantages of distributed revision control" +#~ msgstr "分布版本控制的优点" + +#~ msgid "Advantages for open source projects" +#~ msgstr "开源项目的优点" + +#~ msgid "Advantages for commercial projects" +#~ msgstr "商业项目的优点" + +#~ msgid "Why choose Mercurial?" +#~ msgstr "为什么选择 Mercurial?" + +#~ msgid "Mercurial compared with other tools" +#~ msgstr "Mercurial 与其它工具的比较" + +#~ msgid "Commercial tools" +#~ msgstr "商业工具" + +#~ msgid "Choosing a revision control tool" +#~ msgstr "选择版本控制工具" + +#~ msgid "Switching from another tool to Mercurial" +#~ msgstr "从其它工具切换到 Mercurial"
--- a/web/hgbook/dbutil.py Fri Mar 20 15:40:06 2009 +0800 +++ b/web/hgbook/dbutil.py Fri Mar 20 16:43:35 2009 +0800 @@ -1,4 +1,5 @@ import MySQLdb as mysql +import sys def connect(): try:
--- a/web/hgbook/load_elements.py Fri Mar 20 15:40:06 2009 +0800 +++ b/web/hgbook/load_elements.py Fri Mar 20 16:43:35 2009 +0800 @@ -8,7 +8,7 @@ sys.path.append(os.path.dirname(__file__)) import dbutil -os.system('make -C ../../en ids') +os.system('make -C ../../en all-ids.dat') conn = dbutil.connect() c = conn.cursor()