Bryan O'Sullivan avatar Bryan O'Sullivan committed 9fd0c59

Add to hook chapter.
Document each macro in 99defs.tex.

Comments (0)

Files changed (2)

+% Bug ID.
 \newcommand{\bug}[1]{\index{Mercurial issue!no.~#1}\href{http://www.selenic.com/mercurial/bts/issue#1}{Mercurial issue no.~#1}}
+
+% File name in the user's home directory.
 \newcommand{\tildefile}[1]{\texttt{\~{}/#1}}
+
+% File name.
 \newcommand{\filename}[1]{\texttt{#1}}
+
+% Directory name.
 \newcommand{\dirname}[1]{\texttt{#1}}
+
+% File name, with index entry.
+% The ``s'' prefix comes from ``special''.
 \newcommand{\sfilename}[1]{\index{\texttt{#1} file}\texttt{#1}}
+
+% Directory name, with index entry.
 \newcommand{\sdirname}[1]{\index{\texttt{#1} directory}\texttt{#1}}
+
+% Mercurial extension.
 \newcommand{\hgext}[1]{\index{\texttt{#1} extension}\texttt{#1}}
+
+% Mercurial command.
 \newcommand{\hgcmd}[1]{\index{\texttt{#1} command}``\texttt{hg #1}''}
+
+% Mercurial command, with arguments.
+\newcommand{\hgcmdargs}[2]{\index{\texttt{#1} command}``\texttt{hg #1 #2}''}
+
+% Shell/system command.
 \newcommand{\command}[1]{\index{\texttt{#1} command}\texttt{#1}}
+
+% Shell/system command, with arguments.
 \newcommand{\cmdargs}[2]{\index{\texttt{#1} command}``\texttt{#1 #2}''}
-\newcommand{\hgcmdargs}[2]{\index{\texttt{#1} command}``\texttt{hg #1 #2}''}
+
+% Mercurial command option.
 \newcommand{\hgopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}}
+
+% Mercurial global option.
+\newcommand{\hggopt}[1]{\index{global options!\texttt{#1} option}\texttt{#1}}
+
+% Shell/system command option.
 \newcommand{\cmdopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}}
+
+% Command option.
 \newcommand{\option}[1]{\texttt{#1}}
+
+% Software package.
 \newcommand{\package}[1]{\index{\texttt{#1} package}\texttt{#1}}
+
+% Section name from a hgrc file.
 \newcommand{\rcsection}[1]{\index{\texttt{hgrc} file!\texttt{#1} section}\texttt{[#1]}}
+
+% Named item in a hgrc file section.
 \newcommand{\rcitem}[2]{\index{\texttt{hgrc} file!\texttt{#1}
     section!\texttt{#2} entry}\texttt{#1.#2}}
+
+% hgrc file.
 \newcommand{\hgrc}{\index{\texttt{hgrc} file}\texttt{hgrc}}
+
+% Hook name.
 \newcommand{\hook}[1]{\index{\texttt{#1} hook}\index{hooks!\texttt{#1}}\texttt{#1}}
+
+% Environment variable.
 \newcommand{\envar}[1]{\index{\texttt{#1} environment
     variable}\index{environment variables!\texttt{#1}}\texttt{#1}}
 
+% Python module.
+\newcommand{\pymod}[1]{\index{\texttt{#1} module}\texttt{#1}}
+
+% Python class in a module.
+\newcommand{\pymodclass}[2]{\index{\texttt{#1} module!\texttt{#2}
+    class}\texttt{#1.#2}}
+
+% Note: blah blah.
 \newsavebox{\notebox}
 \newenvironment{note}%
   {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Note:}\space}%
   {\end{minipage}\end{lrbox}\fbox{\usebox{\notebox}}}
 
+% Code sample, eating 4 characters of leading space.
 \DefineVerbatimEnvironment{codesample4}{Verbatim}{frame=single,gobble=4,numbers=left,commandchars=\\\{\}}
+
+% Code sample, eating 2 characters of leading space.
+\DefineVerbatimEnvironment{codesample2}{Verbatim}{frame=single,gobble=2,numbers=left,commandchars=\\\{\}}
+
+% Interaction from the examples directory.
 \newcommand{\interaction}[1]{\VerbatimInput[frame=single,numbers=left,commandchars=\\\{\}]{examples/#1.out}}
 
+% Graphics inclusion.
 \ifpdf
   \newcommand{\grafix}[1]{\includegraphics{#1}}
 \else
 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}
+\section{Writing your own hooks}
+
+When you are writing a hook, you might find it useful to run Mercurial
+either with the \hggopt{-v} option, or the \rcitem{ui}{verbose} config
+item set to ``true''.  When you do so, Mercurial will print a message
+before it calls each hook.
+
+\subsection{Choosing how your hook should run}
+\label{sec:hook:lang}
 
 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
+script---or as a Python function that is executed within the Mercurial
 process.
 
 Writing a hook as an external program has the advantage that it
 performance (probably the majority of hooks), a shell script is
 perfectly fine.
 
-\section{Hook parameters}
+\subsection{Hook parameters}
 \label{sec:hook:param}
 
 Mercurial calls each hook with a set of well-defined parameters.  In
 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'')
+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 ``true'') or 0 (for ``false'') as an environment
+variable for an external hook.  If a hook parameter is named
+\texttt{foo}, the keyword argument for a Python hook will also be
+named \texttt{foo} Python, while the environment variable for an
+external hook will be named \texttt{HG\_FOO}.
+
+\subsection{Hook return values and activity control}
+
+A hook that executes successfully must exit with a status of zero if
+external, or return boolean ``false'' if in-process.  Failure is
+indicated with a non-zero exit status from an external hook, or an
+in-process hook returning boolean ``true''.  If an in-process hook
+raises an exception, the hook is considered to have failed.
+
+For a hook that controls whether an activity can proceed, zero/false
+means ``allow'', while non-zero/true/exception means ``deny''.
+
+\subsection{Writing an external hook}
+
+When you define an external hook in your \hgrc\ 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.
+
+An executable hook is always run with its current directory set to a
+repository's root directory.
+
+Each hook parameter is passed in as an environment variable; the name
+is upper-cased, and prefixed with the string ``\texttt{HG\_}''.
+
+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.
+
+\subsection{Telling Mercurial to use an in-process hook}
+
+The \hgrc\ 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 ``\texttt{python:}'', and continue with the
+fully-qualified name of a callable object to use as the hook's value.
+
+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}
+right, it should ``just work''.
+
+The following \hgrc\ example snippet illustrates the syntax and
+meaning of the notions we just described.
+\begin{codesample2}
+  [hooks]
+  commit.example = python:mymodule.submodule.myhook
+\end{codesample2}
+When Mercurial runs the \texttt{commit.example} hook, it imports
+\texttt{mymodule.submodule}, looks for the callable object named
+\texttt{myhook}, and calls it.
+
+\subsection{Writing an in-process hook}
+
+The simplest in-process hook does nothing, but illustrates the basic
+shape of the hook API:
+\begin{codesample2}
+  def myhook(ui, repo, **kwargs):
+      pass
+\end{codesample2}
+The first argument to a Python hook is always a
+\pymodclass{mercurial.ui}{ui} object.  The second is a repository object;
+at the moment, it is always an instance of
+\pymodclass{mercurial.localrepo}{localrepository}.  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 \texttt{**kwargs} above.
 
 
 %%% Local Variables: 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.