changeset 34:c0979ed1eabd

Get started on hook chapter.
author Bryan O'Sullivan <bos@serpentine.com>
date Sun, 16 Jul 2006 00:01:43 -0700
parents a17b0f38286d
children e68f4a96c16e b8539d91c84d
files en/00book.tex en/99defs.tex en/Makefile en/examples/hook.simple en/hook.tex
diffstat 5 files changed, 185 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/en/00book.tex	Sun Jul 16 00:01:21 2006 -0700
+++ b/en/00book.tex	Sun Jul 16 00:01:43 2006 -0700
@@ -37,6 +37,7 @@
 
 \include{preface}
 \include{intro}
+\include{hook}
 \include{mq}
 
 \appendix
--- a/en/99defs.tex	Sun Jul 16 00:01:21 2006 -0700
+++ b/en/99defs.tex	Sun Jul 16 00:01:43 2006 -0700
@@ -13,6 +13,13 @@
 \newcommand{\cmdopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}}
 \newcommand{\option}[1]{\texttt{#1}}
 \newcommand{\package}[1]{\index{\texttt{#1} package}\texttt{#1}}
+\newcommand{\rcsection}[1]{\index{\texttt{hgrc} file!\texttt{#1} section}\texttt{[#1]}}
+\newcommand{\rcitem}[2]{\index{\texttt{hgrc} file!\texttt{#1}
+    section!\texttt{#2} entry}\texttt{#1.#2}}
+\newcommand{\hgrc}{\index{\texttt{hgrc} file}\texttt{hgrc}}
+\newcommand{\hook}[1]{\index{\texttt{#1} hook}\index{hooks!\texttt{#1}}\texttt{#1}}
+\newcommand{\envar}[1]{\index{\texttt{#1} environment
+    variable}\index{environment variables!\texttt{#1}}\texttt{#1}}
 
 \newsavebox{\notebox}
 \newenvironment{note}%
--- a/en/Makefile	Sun Jul 16 00:01:21 2006 -0700
+++ b/en/Makefile	Sun Jul 16 00:01:43 2006 -0700
@@ -6,16 +6,18 @@
 	00book.tex \
 	99book.bib \
 	99defs.tex \
-	preface.tex \
+	build_id.tex \
+	hook.tex \
 	intro.tex \
 	mq.tex \
-	build_id.tex
+	preface.tex
 
 image-sources := \
 	mq-stack.svg
 
 example-sources := \
 	examples/run-example \
+	examples/hook.simple \
 	examples/mq.qinit-help \
 	examples/mq.diff \
 	examples/mq.tarball \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/en/examples/hook.simple	Sun Jul 16 00:01:43 2006 -0700
@@ -0,0 +1,34 @@
+#$ name: init
+
+hg init hook-test
+cd hook-test
+echo '[hooks]' >> .hg/hgrc
+echo 'commit = echo committed $HG_NODE' >> .hg/hgrc
+cat .hg/hgrc
+echo a > a
+hg add a
+hg commit -m 'testing commit hook'
+
+#$ name: ext
+
+echo 'commit.when = echo "date of commit:"; date' >> .hg/hgrc
+echo a >> a
+hg commit -m 'i have two hooks'
+
+#$ name:
+
+echo '#!/bin/sh' >> check_bug_id
+echo '# check that a commit comment mentions a numeric bug id' >> check_bug_id
+echo 'hg log -r $1 --template {desc} | grep -q "\<bug *[0-9]"' >> check_bug_id
+chmod +x check_bug_id
+
+#$ name: pretxncommit
+
+cat check_bug_id
+
+echo 'pretxncommit.bug_id_required = ./check_bug_id $HG_NODE' >> .hg/hgrc
+
+echo a >> a
+hg commit -m 'i am not mentioning a bug id'
+
+hg commit -m 'i refer you to bug 666'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/en/hook.tex	Sun Jul 16 00:01:43 2006 -0700
@@ -0,0 +1,139 @@
+\chapter{Handling repository events with hooks}
+\label{chap:hook}
+
+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.
+
+The name Mercurial uses for one of these actions is a \emph{hook}.
+Hooks are called ``triggers'' in some revision control systems, but
+the two names refer to the same idea.
+
+\section{A short tutorial on using hooks}
+\label{sec:hook:simple}
+
+It is easy to write a Mercurial hook.  Let's start with a hook that
+runs when you finish a \hgcmd{commit}, and simply prints the hash of
+the changeset you just created.  The hook is called \hook{commit}.
+
+\begin{figure}[ht]
+  \interaction{hook.simple.init}
+  \caption{A simple hook that runs when a changeset is committed}
+  \label{ex:hook:init}
+\end{figure}
+
+All hooks follow the pattern in example~\ref{ex:hook:init}.  You add
+an entry to the \rcsection{hooks} section of your \hgrc\.  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} in the example).
+
+\subsection{Performing multiple actions per event}
+
+Quite often, you will want to define more than one hook for a
+particular kind of event, as shown in example~\ref{ex:hook:ext}.
+Mercurial lets you do this by adding an \emph{extension} 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 ``\texttt{.}'' character), followed
+by some more text of your choosing.  For example, Mercurial will run
+both \texttt{commit.foo} and \texttt{commit.bar} when the
+\texttt{commit} event occurs.
+
+\begin{figure}[ht]
+  \interaction{hook.simple.ext}
+  \caption{Defining a second \hook{commit} hook}
+  \label{ex:hook:ext}
+\end{figure}
+
+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 \texttt{commit.bar} before
+\texttt{commit.foo}, and \texttt{commit} before both.
+
+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~\ref{sec:hook:perm} for an example).
+
+\subsection{Controlling whether an activity can proceed}
+\label{sec:hook:perm}
+
+In our earlier examples, we used the \hook{commit} 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.
+
+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.  
+
+The \hook{pretxncommit} 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 \hook{pretxncommit} hook has the ability to
+decide whether the transaction can complete, or must be rolled back.
+
+If the \hook{pretxncommit} hook exits with a status code of zero, the
+transaction is allowed to complete; the commit finishes; and the
+\hook{commit} hook is run.  If the \hook{pretxncommit} hook exits with
+a non-zero status code, the transaction is rolled back; the metadata
+representing the changeset is erased; and the \hook{commit} hook is
+not run.
+
+\begin{figure}[ht]
+  \interaction{hook.simple.pretxncommit}
+  \caption{Using the \hook{pretxncommit} hook to control commits}
+  \label{ex:hook:pretxncommit}
+\end{figure}
+
+The hook in example~\ref{ex:hook:pretxncommit} checks that a commit
+comment contains a bug ID.  If it does, the commit can complete.  If
+not, the commit is rolled back.
+
+\section{Choosing how to write a hook}
+\label{sec:hook:impl}
+
+You can write a hook either as a normal program---typically a shell
+script---or as a Python function that is called within the Mercurial
+process.
+
+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.
+
+An in-process Python hook has complete access to the Mercurial API,
+and does not ``shell out'' 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.
+
+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.
+
+\section{Hook parameters}
+\label{sec:hook:param}
+
+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.
+
+Whether your hook is written in Python or as a shell script, the
+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
+``true'') or 0 (for ``false'')
+
+
+%%% Local Variables: 
+%%% mode: latex
+%%% TeX-master: "00book"
+%%% End: