hgbook

changeset 34:c0979ed1eabd

Get started on hook chapter.
author Bryan O'Sullivan <bos@serpentine.com>
date Sun Jul 16 00:01:43 2006 -0700 (2006-07-16)
parents a17b0f38286d
children e68f4a96c16e b8539d91c84d
files en/00book.tex en/99defs.tex en/Makefile en/examples/hook.simple en/hook.tex
line diff
     1.1 --- a/en/00book.tex	Sun Jul 16 00:01:21 2006 -0700
     1.2 +++ b/en/00book.tex	Sun Jul 16 00:01:43 2006 -0700
     1.3 @@ -37,6 +37,7 @@
     1.4  
     1.5  \include{preface}
     1.6  \include{intro}
     1.7 +\include{hook}
     1.8  \include{mq}
     1.9  
    1.10  \appendix
     2.1 --- a/en/99defs.tex	Sun Jul 16 00:01:21 2006 -0700
     2.2 +++ b/en/99defs.tex	Sun Jul 16 00:01:43 2006 -0700
     2.3 @@ -13,6 +13,13 @@
     2.4  \newcommand{\cmdopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}}
     2.5  \newcommand{\option}[1]{\texttt{#1}}
     2.6  \newcommand{\package}[1]{\index{\texttt{#1} package}\texttt{#1}}
     2.7 +\newcommand{\rcsection}[1]{\index{\texttt{hgrc} file!\texttt{#1} section}\texttt{[#1]}}
     2.8 +\newcommand{\rcitem}[2]{\index{\texttt{hgrc} file!\texttt{#1}
     2.9 +    section!\texttt{#2} entry}\texttt{#1.#2}}
    2.10 +\newcommand{\hgrc}{\index{\texttt{hgrc} file}\texttt{hgrc}}
    2.11 +\newcommand{\hook}[1]{\index{\texttt{#1} hook}\index{hooks!\texttt{#1}}\texttt{#1}}
    2.12 +\newcommand{\envar}[1]{\index{\texttt{#1} environment
    2.13 +    variable}\index{environment variables!\texttt{#1}}\texttt{#1}}
    2.14  
    2.15  \newsavebox{\notebox}
    2.16  \newenvironment{note}%
     3.1 --- a/en/Makefile	Sun Jul 16 00:01:21 2006 -0700
     3.2 +++ b/en/Makefile	Sun Jul 16 00:01:43 2006 -0700
     3.3 @@ -6,16 +6,18 @@
     3.4  	00book.tex \
     3.5  	99book.bib \
     3.6  	99defs.tex \
     3.7 -	preface.tex \
     3.8 +	build_id.tex \
     3.9 +	hook.tex \
    3.10  	intro.tex \
    3.11  	mq.tex \
    3.12 -	build_id.tex
    3.13 +	preface.tex
    3.14  
    3.15  image-sources := \
    3.16  	mq-stack.svg
    3.17  
    3.18  example-sources := \
    3.19  	examples/run-example \
    3.20 +	examples/hook.simple \
    3.21  	examples/mq.qinit-help \
    3.22  	examples/mq.diff \
    3.23  	examples/mq.tarball \
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/en/examples/hook.simple	Sun Jul 16 00:01:43 2006 -0700
     4.3 @@ -0,0 +1,34 @@
     4.4 +#$ name: init
     4.5 +
     4.6 +hg init hook-test
     4.7 +cd hook-test
     4.8 +echo '[hooks]' >> .hg/hgrc
     4.9 +echo 'commit = echo committed $HG_NODE' >> .hg/hgrc
    4.10 +cat .hg/hgrc
    4.11 +echo a > a
    4.12 +hg add a
    4.13 +hg commit -m 'testing commit hook'
    4.14 +
    4.15 +#$ name: ext
    4.16 +
    4.17 +echo 'commit.when = echo "date of commit:"; date' >> .hg/hgrc
    4.18 +echo a >> a
    4.19 +hg commit -m 'i have two hooks'
    4.20 +
    4.21 +#$ name:
    4.22 +
    4.23 +echo '#!/bin/sh' >> check_bug_id
    4.24 +echo '# check that a commit comment mentions a numeric bug id' >> check_bug_id
    4.25 +echo 'hg log -r $1 --template {desc} | grep -q "\<bug *[0-9]"' >> check_bug_id
    4.26 +chmod +x check_bug_id
    4.27 +
    4.28 +#$ name: pretxncommit
    4.29 +
    4.30 +cat check_bug_id
    4.31 +
    4.32 +echo 'pretxncommit.bug_id_required = ./check_bug_id $HG_NODE' >> .hg/hgrc
    4.33 +
    4.34 +echo a >> a
    4.35 +hg commit -m 'i am not mentioning a bug id'
    4.36 +
    4.37 +hg commit -m 'i refer you to bug 666'
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/en/hook.tex	Sun Jul 16 00:01:43 2006 -0700
     5.3 @@ -0,0 +1,139 @@
     5.4 +\chapter{Handling repository events with hooks}
     5.5 +\label{chap:hook}
     5.6 +
     5.7 +Mercurial offers a powerful mechanism to let you perform automated
     5.8 +actions in response to events that occur in a repository.  In some
     5.9 +cases, you can even control Mercurial's response to those events.
    5.10 +
    5.11 +The name Mercurial uses for one of these actions is a \emph{hook}.
    5.12 +Hooks are called ``triggers'' in some revision control systems, but
    5.13 +the two names refer to the same idea.
    5.14 +
    5.15 +\section{A short tutorial on using hooks}
    5.16 +\label{sec:hook:simple}
    5.17 +
    5.18 +It is easy to write a Mercurial hook.  Let's start with a hook that
    5.19 +runs when you finish a \hgcmd{commit}, and simply prints the hash of
    5.20 +the changeset you just created.  The hook is called \hook{commit}.
    5.21 +
    5.22 +\begin{figure}[ht]
    5.23 +  \interaction{hook.simple.init}
    5.24 +  \caption{A simple hook that runs when a changeset is committed}
    5.25 +  \label{ex:hook:init}
    5.26 +\end{figure}
    5.27 +
    5.28 +All hooks follow the pattern in example~\ref{ex:hook:init}.  You add
    5.29 +an entry to the \rcsection{hooks} section of your \hgrc\.  On the left
    5.30 +is the name of the event to trigger on; on the right is the action to
    5.31 +take.  As you can see, you can run an arbitrary shell command in a
    5.32 +hook.  Mercurial passes extra information to the hook using
    5.33 +environment variables (look for \envar{HG\_NODE} in the example).
    5.34 +
    5.35 +\subsection{Performing multiple actions per event}
    5.36 +
    5.37 +Quite often, you will want to define more than one hook for a
    5.38 +particular kind of event, as shown in example~\ref{ex:hook:ext}.
    5.39 +Mercurial lets you do this by adding an \emph{extension} to the end of
    5.40 +a hook's name.  You extend a hook's name by giving the name of the
    5.41 +hook, followed by a full stop (the ``\texttt{.}'' character), followed
    5.42 +by some more text of your choosing.  For example, Mercurial will run
    5.43 +both \texttt{commit.foo} and \texttt{commit.bar} when the
    5.44 +\texttt{commit} event occurs.
    5.45 +
    5.46 +\begin{figure}[ht]
    5.47 +  \interaction{hook.simple.ext}
    5.48 +  \caption{Defining a second \hook{commit} hook}
    5.49 +  \label{ex:hook:ext}
    5.50 +\end{figure}
    5.51 +
    5.52 +To give a well-defined order of execution when there are multiple
    5.53 +hooks defined for an event, Mercurial sorts hooks by extension, and
    5.54 +executes the hook commands in this sorted order.  In the above
    5.55 +example, it will execute \texttt{commit.bar} before
    5.56 +\texttt{commit.foo}, and \texttt{commit} before both.
    5.57 +
    5.58 +It is a good idea to use a somewhat descriptive extension when you
    5.59 +define a new hook.  This will help you to remember what the hook was
    5.60 +for.  If the hook fails, you'll get an error message that contains the
    5.61 +hook name and extension, so using a descriptive extension could give
    5.62 +you an immediate hint as to why the hook failed (see
    5.63 +section~\ref{sec:hook:perm} for an example).
    5.64 +
    5.65 +\subsection{Controlling whether an activity can proceed}
    5.66 +\label{sec:hook:perm}
    5.67 +
    5.68 +In our earlier examples, we used the \hook{commit} hook, which is
    5.69 +run after a commit has completed.  This is one of several Mercurial
    5.70 +hooks that run after an activity finishes.  Such hooks have no way of
    5.71 +influencing the activity itself.
    5.72 +
    5.73 +Mercurial defines a number of events that occur before an activity
    5.74 +starts; or after it starts, but before it finishes.  Hooks that
    5.75 +trigger on these events have the added ability to choose whether the
    5.76 +activity can continue, or will abort.  
    5.77 +
    5.78 +The \hook{pretxncommit} hook runs after a commit has all but
    5.79 +completed.  In other words, the metadata representing the changeset
    5.80 +has been written out to disk, but the transaction has not yet been
    5.81 +allowed to complete.  The \hook{pretxncommit} hook has the ability to
    5.82 +decide whether the transaction can complete, or must be rolled back.
    5.83 +
    5.84 +If the \hook{pretxncommit} hook exits with a status code of zero, the
    5.85 +transaction is allowed to complete; the commit finishes; and the
    5.86 +\hook{commit} hook is run.  If the \hook{pretxncommit} hook exits with
    5.87 +a non-zero status code, the transaction is rolled back; the metadata
    5.88 +representing the changeset is erased; and the \hook{commit} hook is
    5.89 +not run.
    5.90 +
    5.91 +\begin{figure}[ht]
    5.92 +  \interaction{hook.simple.pretxncommit}
    5.93 +  \caption{Using the \hook{pretxncommit} hook to control commits}
    5.94 +  \label{ex:hook:pretxncommit}
    5.95 +\end{figure}
    5.96 +
    5.97 +The hook in example~\ref{ex:hook:pretxncommit} checks that a commit
    5.98 +comment contains a bug ID.  If it does, the commit can complete.  If
    5.99 +not, the commit is rolled back.
   5.100 +
   5.101 +\section{Choosing how to write a hook}
   5.102 +\label{sec:hook:impl}
   5.103 +
   5.104 +You can write a hook either as a normal program---typically a shell
   5.105 +script---or as a Python function that is called within the Mercurial
   5.106 +process.
   5.107 +
   5.108 +Writing a hook as an external program has the advantage that it
   5.109 +requires no knowledge of Mercurial's internals.  You can call normal
   5.110 +Mercurial commands to get any added information you need.  The
   5.111 +trade-off is that external hooks are slower than in-process hooks.
   5.112 +
   5.113 +An in-process Python hook has complete access to the Mercurial API,
   5.114 +and does not ``shell out'' to another process, so it is inherently
   5.115 +faster than an external hook.  It is also easier to obtain much of the
   5.116 +information that a hook requires by using the Mercurial API than by
   5.117 +running Mercurial commands.
   5.118 +
   5.119 +If you are comfortable with Python, or require high performance,
   5.120 +writing your hooks in Python may be a good choice.  However, when you
   5.121 +have a straightforward hook to write and you don't need to care about
   5.122 +performance (probably the majority of hooks), a shell script is
   5.123 +perfectly fine.
   5.124 +
   5.125 +\section{Hook parameters}
   5.126 +\label{sec:hook:param}
   5.127 +
   5.128 +Mercurial calls each hook with a set of well-defined parameters.  In
   5.129 +Python, a parameter is passed as a keyword argument to your hook
   5.130 +function.  For an external program, a parameter is passed as an
   5.131 +environment variable.
   5.132 +
   5.133 +Whether your hook is written in Python or as a shell script, the
   5.134 +parameter names and values will be the same.  A boolean parameter will
   5.135 +be represented as a boolean value in Python, but as the number 1 (for
   5.136 +``true'') or 0 (for ``false'')
   5.137 +
   5.138 +
   5.139 +%%% Local Variables: 
   5.140 +%%% mode: latex
   5.141 +%%% TeX-master: "00book"
   5.142 +%%% End: