
bos@34: \chapter{Handling repository events with hooks}
bos@34: \label{chap:hook}
bos@34: 
bos@34: Mercurial offers a powerful mechanism to let you perform automated
bos@34: actions in response to events that occur in a repository.  In some
bos@34: cases, you can even control Mercurial's response to those events.
bos@34: 
bos@34: The name Mercurial uses for one of these actions is a \emph{hook}.
bos@34: Hooks are called ``triggers'' in some revision control systems, but
bos@34: the two names refer to the same idea.
bos@34: 
bos@34: \section{A short tutorial on using hooks}
bos@34: \label{sec:hook:simple}
bos@34: 
bos@34: It is easy to write a Mercurial hook.  Let's start with a hook that
bos@34: runs when you finish a \hgcmd{commit}, and simply prints the hash of
bos@34: the changeset you just created.  The hook is called \hook{commit}.
bos@34: 
bos@34: \begin{figure}[ht]
bos@34:   \interaction{hook.simple.init}
bos@34:   \caption{A simple hook that runs when a changeset is committed}
bos@34:   \label{ex:hook:init}
bos@34: \end{figure}
bos@34: 
bos@34: All hooks follow the pattern in example~\ref{ex:hook:init}.  You add
bos@34: an entry to the \rcsection{hooks} section of your \hgrc\.  On the left
bos@34: is the name of the event to trigger on; on the right is the action to
bos@34: take.  As you can see, you can run an arbitrary shell command in a
bos@34: hook.  Mercurial passes extra information to the hook using
bos@34: environment variables (look for \envar{HG\_NODE} in the example).
bos@34: 
bos@34: \subsection{Performing multiple actions per event}
bos@34: 
bos@34: Quite often, you will want to define more than one hook for a
bos@34: particular kind of event, as shown in example~\ref{ex:hook:ext}.
bos@34: Mercurial lets you do this by adding an \emph{extension} to the end of
bos@34: a hook's name.  You extend a hook's name by giving the name of the
bos@34: hook, followed by a full stop (the ``\texttt{.}'' character), followed
bos@34: by some more text of your choosing.  For example, Mercurial will run
bos@34: both \texttt{commit.foo} and \texttt{commit.bar} when the
bos@34: \texttt{commit} event occurs.
bos@34: 
bos@34: \begin{figure}[ht]
bos@34:   \interaction{hook.simple.ext}
bos@34:   \caption{Defining a second \hook{commit} hook}
bos@34:   \label{ex:hook:ext}
bos@34: \end{figure}
bos@34: 
bos@34: To give a well-defined order of execution when there are multiple
bos@34: hooks defined for an event, Mercurial sorts hooks by extension, and
bos@34: executes the hook commands in this sorted order.  In the above
bos@34: example, it will execute \texttt{commit.bar} before
bos@34: \texttt{commit.foo}, and \texttt{commit} before both.
bos@34: 
bos@34: It is a good idea to use a somewhat descriptive extension when you
bos@34: define a new hook.  This will help you to remember what the hook was
bos@34: for.  If the hook fails, you'll get an error message that contains the
bos@34: hook name and extension, so using a descriptive extension could give
bos@34: you an immediate hint as to why the hook failed (see
bos@34: section~\ref{sec:hook:perm} for an example).
bos@34: 
bos@34: \subsection{Controlling whether an activity can proceed}
bos@34: \label{sec:hook:perm}
bos@34: 
bos@34: In our earlier examples, we used the \hook{commit} hook, which is
bos@34: run after a commit has completed.  This is one of several Mercurial
bos@34: hooks that run after an activity finishes.  Such hooks have no way of
bos@34: influencing the activity itself.
bos@34: 
bos@34: Mercurial defines a number of events that occur before an activity
bos@34: starts; or after it starts, but before it finishes.  Hooks that
bos@34: trigger on these events have the added ability to choose whether the
bos@34: activity can continue, or will abort.  
bos@34: 
bos@34: The \hook{pretxncommit} hook runs after a commit has all but
bos@34: completed.  In other words, the metadata representing the changeset
bos@34: has been written out to disk, but the transaction has not yet been
bos@34: allowed to complete.  The \hook{pretxncommit} hook has the ability to
bos@34: decide whether the transaction can complete, or must be rolled back.
bos@34: 
bos@34: If the \hook{pretxncommit} hook exits with a status code of zero, the
bos@34: transaction is allowed to complete; the commit finishes; and the
bos@34: \hook{commit} hook is run.  If the \hook{pretxncommit} hook exits with
bos@34: a non-zero status code, the transaction is rolled back; the metadata
bos@34: representing the changeset is erased; and the \hook{commit} hook is
bos@34: not run.
bos@34: 
bos@34: \begin{figure}[ht]
bos@34:   \interaction{hook.simple.pretxncommit}
bos@34:   \caption{Using the \hook{pretxncommit} hook to control commits}
bos@34:   \label{ex:hook:pretxncommit}
bos@34: \end{figure}
bos@34: 
bos@34: The hook in example~\ref{ex:hook:pretxncommit} checks that a commit
bos@34: comment contains a bug ID.  If it does, the commit can complete.  If
bos@34: not, the commit is rolled back.
bos@34: 
bos@34: \section{Choosing how to write a hook}
bos@34: \label{sec:hook:impl}
bos@34: 
bos@34: You can write a hook either as a normal program---typically a shell
bos@34: script---or as a Python function that is called within the Mercurial
bos@34: process.
bos@34: 
bos@34: Writing a hook as an external program has the advantage that it
bos@34: requires no knowledge of Mercurial's internals.  You can call normal
bos@34: Mercurial commands to get any added information you need.  The
bos@34: trade-off is that external hooks are slower than in-process hooks.
bos@34: 
bos@34: An in-process Python hook has complete access to the Mercurial API,
bos@34: and does not ``shell out'' to another process, so it is inherently
bos@34: faster than an external hook.  It is also easier to obtain much of the
bos@34: information that a hook requires by using the Mercurial API than by
bos@34: running Mercurial commands.
bos@34: 
bos@34: If you are comfortable with Python, or require high performance,
bos@34: writing your hooks in Python may be a good choice.  However, when you
bos@34: have a straightforward hook to write and you don't need to care about
bos@34: performance (probably the majority of hooks), a shell script is
bos@34: perfectly fine.
bos@34: 
bos@34: \section{Hook parameters}
bos@34: \label{sec:hook:param}
bos@34: 
bos@34: Mercurial calls each hook with a set of well-defined parameters.  In
bos@34: Python, a parameter is passed as a keyword argument to your hook
bos@34: function.  For an external program, a parameter is passed as an
bos@34: environment variable.
bos@34: 
bos@34: Whether your hook is written in Python or as a shell script, the
bos@34: parameter names and values will be the same.  A boolean parameter will
bos@34: be represented as a boolean value in Python, but as the number 1 (for
bos@34: ``true'') or 0 (for ``false'')
bos@34: 
bos@34: 
bos@34: %%% Local Variables: 
bos@34: %%% mode: latex
bos@34: %%% TeX-master: "00book"
bos@34: %%% End: 




