hgbook
annotate en/hook.tex @ 37:9fd0c59b009a
Add to hook chapter.
Document each macro in 99defs.tex.
Document each macro in 99defs.tex.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Mon Jul 17 00:01:01 2006 -0700 (2006-07-17) |
parents | c0979ed1eabd |
children | b49a7dd4e564 |
rev | line source |
---|---|
bos@34 | 1 \chapter{Handling repository events with hooks} |
bos@34 | 2 \label{chap:hook} |
bos@34 | 3 |
bos@34 | 4 Mercurial offers a powerful mechanism to let you perform automated |
bos@34 | 5 actions in response to events that occur in a repository. In some |
bos@34 | 6 cases, you can even control Mercurial's response to those events. |
bos@34 | 7 |
bos@34 | 8 The name Mercurial uses for one of these actions is a \emph{hook}. |
bos@34 | 9 Hooks are called ``triggers'' in some revision control systems, but |
bos@34 | 10 the two names refer to the same idea. |
bos@34 | 11 |
bos@34 | 12 \section{A short tutorial on using hooks} |
bos@34 | 13 \label{sec:hook:simple} |
bos@34 | 14 |
bos@34 | 15 It is easy to write a Mercurial hook. Let's start with a hook that |
bos@34 | 16 runs when you finish a \hgcmd{commit}, and simply prints the hash of |
bos@34 | 17 the changeset you just created. The hook is called \hook{commit}. |
bos@34 | 18 |
bos@34 | 19 \begin{figure}[ht] |
bos@34 | 20 \interaction{hook.simple.init} |
bos@34 | 21 \caption{A simple hook that runs when a changeset is committed} |
bos@34 | 22 \label{ex:hook:init} |
bos@34 | 23 \end{figure} |
bos@34 | 24 |
bos@34 | 25 All hooks follow the pattern in example~\ref{ex:hook:init}. You add |
bos@34 | 26 an entry to the \rcsection{hooks} section of your \hgrc\. On the left |
bos@34 | 27 is the name of the event to trigger on; on the right is the action to |
bos@34 | 28 take. As you can see, you can run an arbitrary shell command in a |
bos@34 | 29 hook. Mercurial passes extra information to the hook using |
bos@34 | 30 environment variables (look for \envar{HG\_NODE} in the example). |
bos@34 | 31 |
bos@34 | 32 \subsection{Performing multiple actions per event} |
bos@34 | 33 |
bos@34 | 34 Quite often, you will want to define more than one hook for a |
bos@34 | 35 particular kind of event, as shown in example~\ref{ex:hook:ext}. |
bos@34 | 36 Mercurial lets you do this by adding an \emph{extension} to the end of |
bos@34 | 37 a hook's name. You extend a hook's name by giving the name of the |
bos@34 | 38 hook, followed by a full stop (the ``\texttt{.}'' character), followed |
bos@34 | 39 by some more text of your choosing. For example, Mercurial will run |
bos@34 | 40 both \texttt{commit.foo} and \texttt{commit.bar} when the |
bos@34 | 41 \texttt{commit} event occurs. |
bos@34 | 42 |
bos@34 | 43 \begin{figure}[ht] |
bos@34 | 44 \interaction{hook.simple.ext} |
bos@34 | 45 \caption{Defining a second \hook{commit} hook} |
bos@34 | 46 \label{ex:hook:ext} |
bos@34 | 47 \end{figure} |
bos@34 | 48 |
bos@34 | 49 To give a well-defined order of execution when there are multiple |
bos@34 | 50 hooks defined for an event, Mercurial sorts hooks by extension, and |
bos@34 | 51 executes the hook commands in this sorted order. In the above |
bos@34 | 52 example, it will execute \texttt{commit.bar} before |
bos@34 | 53 \texttt{commit.foo}, and \texttt{commit} before both. |
bos@34 | 54 |
bos@34 | 55 It is a good idea to use a somewhat descriptive extension when you |
bos@34 | 56 define a new hook. This will help you to remember what the hook was |
bos@34 | 57 for. If the hook fails, you'll get an error message that contains the |
bos@34 | 58 hook name and extension, so using a descriptive extension could give |
bos@34 | 59 you an immediate hint as to why the hook failed (see |
bos@34 | 60 section~\ref{sec:hook:perm} for an example). |
bos@34 | 61 |
bos@34 | 62 \subsection{Controlling whether an activity can proceed} |
bos@34 | 63 \label{sec:hook:perm} |
bos@34 | 64 |
bos@34 | 65 In our earlier examples, we used the \hook{commit} hook, which is |
bos@34 | 66 run after a commit has completed. This is one of several Mercurial |
bos@34 | 67 hooks that run after an activity finishes. Such hooks have no way of |
bos@34 | 68 influencing the activity itself. |
bos@34 | 69 |
bos@34 | 70 Mercurial defines a number of events that occur before an activity |
bos@34 | 71 starts; or after it starts, but before it finishes. Hooks that |
bos@34 | 72 trigger on these events have the added ability to choose whether the |
bos@34 | 73 activity can continue, or will abort. |
bos@34 | 74 |
bos@34 | 75 The \hook{pretxncommit} hook runs after a commit has all but |
bos@34 | 76 completed. In other words, the metadata representing the changeset |
bos@34 | 77 has been written out to disk, but the transaction has not yet been |
bos@34 | 78 allowed to complete. The \hook{pretxncommit} hook has the ability to |
bos@34 | 79 decide whether the transaction can complete, or must be rolled back. |
bos@34 | 80 |
bos@34 | 81 If the \hook{pretxncommit} hook exits with a status code of zero, the |
bos@34 | 82 transaction is allowed to complete; the commit finishes; and the |
bos@34 | 83 \hook{commit} hook is run. If the \hook{pretxncommit} hook exits with |
bos@34 | 84 a non-zero status code, the transaction is rolled back; the metadata |
bos@34 | 85 representing the changeset is erased; and the \hook{commit} hook is |
bos@34 | 86 not run. |
bos@34 | 87 |
bos@34 | 88 \begin{figure}[ht] |
bos@34 | 89 \interaction{hook.simple.pretxncommit} |
bos@34 | 90 \caption{Using the \hook{pretxncommit} hook to control commits} |
bos@34 | 91 \label{ex:hook:pretxncommit} |
bos@34 | 92 \end{figure} |
bos@34 | 93 |
bos@34 | 94 The hook in example~\ref{ex:hook:pretxncommit} checks that a commit |
bos@34 | 95 comment contains a bug ID. If it does, the commit can complete. If |
bos@34 | 96 not, the commit is rolled back. |
bos@34 | 97 |
bos@37 | 98 \section{Writing your own hooks} |
bos@37 | 99 |
bos@37 | 100 When you are writing a hook, you might find it useful to run Mercurial |
bos@37 | 101 either with the \hggopt{-v} option, or the \rcitem{ui}{verbose} config |
bos@37 | 102 item set to ``true''. When you do so, Mercurial will print a message |
bos@37 | 103 before it calls each hook. |
bos@37 | 104 |
bos@37 | 105 \subsection{Choosing how your hook should run} |
bos@37 | 106 \label{sec:hook:lang} |
bos@34 | 107 |
bos@34 | 108 You can write a hook either as a normal program---typically a shell |
bos@37 | 109 script---or as a Python function that is executed within the Mercurial |
bos@34 | 110 process. |
bos@34 | 111 |
bos@34 | 112 Writing a hook as an external program has the advantage that it |
bos@34 | 113 requires no knowledge of Mercurial's internals. You can call normal |
bos@34 | 114 Mercurial commands to get any added information you need. The |
bos@34 | 115 trade-off is that external hooks are slower than in-process hooks. |
bos@34 | 116 |
bos@34 | 117 An in-process Python hook has complete access to the Mercurial API, |
bos@34 | 118 and does not ``shell out'' to another process, so it is inherently |
bos@34 | 119 faster than an external hook. It is also easier to obtain much of the |
bos@34 | 120 information that a hook requires by using the Mercurial API than by |
bos@34 | 121 running Mercurial commands. |
bos@34 | 122 |
bos@34 | 123 If you are comfortable with Python, or require high performance, |
bos@34 | 124 writing your hooks in Python may be a good choice. However, when you |
bos@34 | 125 have a straightforward hook to write and you don't need to care about |
bos@34 | 126 performance (probably the majority of hooks), a shell script is |
bos@34 | 127 perfectly fine. |
bos@34 | 128 |
bos@37 | 129 \subsection{Hook parameters} |
bos@34 | 130 \label{sec:hook:param} |
bos@34 | 131 |
bos@34 | 132 Mercurial calls each hook with a set of well-defined parameters. In |
bos@34 | 133 Python, a parameter is passed as a keyword argument to your hook |
bos@34 | 134 function. For an external program, a parameter is passed as an |
bos@34 | 135 environment variable. |
bos@34 | 136 |
bos@34 | 137 Whether your hook is written in Python or as a shell script, the |
bos@37 | 138 hook-specific parameter names and values will be the same. A boolean |
bos@37 | 139 parameter will be represented as a boolean value in Python, but as the |
bos@37 | 140 number 1 (for ``true'') or 0 (for ``false'') as an environment |
bos@37 | 141 variable for an external hook. If a hook parameter is named |
bos@37 | 142 \texttt{foo}, the keyword argument for a Python hook will also be |
bos@37 | 143 named \texttt{foo} Python, while the environment variable for an |
bos@37 | 144 external hook will be named \texttt{HG\_FOO}. |
bos@37 | 145 |
bos@37 | 146 \subsection{Hook return values and activity control} |
bos@37 | 147 |
bos@37 | 148 A hook that executes successfully must exit with a status of zero if |
bos@37 | 149 external, or return boolean ``false'' if in-process. Failure is |
bos@37 | 150 indicated with a non-zero exit status from an external hook, or an |
bos@37 | 151 in-process hook returning boolean ``true''. If an in-process hook |
bos@37 | 152 raises an exception, the hook is considered to have failed. |
bos@37 | 153 |
bos@37 | 154 For a hook that controls whether an activity can proceed, zero/false |
bos@37 | 155 means ``allow'', while non-zero/true/exception means ``deny''. |
bos@37 | 156 |
bos@37 | 157 \subsection{Writing an external hook} |
bos@37 | 158 |
bos@37 | 159 When you define an external hook in your \hgrc\ and the hook is run, |
bos@37 | 160 its value is passed to your shell, which interprets it. This means |
bos@37 | 161 that you can use normal shell constructs in the body of the hook. |
bos@37 | 162 |
bos@37 | 163 An executable hook is always run with its current directory set to a |
bos@37 | 164 repository's root directory. |
bos@37 | 165 |
bos@37 | 166 Each hook parameter is passed in as an environment variable; the name |
bos@37 | 167 is upper-cased, and prefixed with the string ``\texttt{HG\_}''. |
bos@37 | 168 |
bos@37 | 169 With the exception of hook parameters, Mercurial does not set or |
bos@37 | 170 modify any environment variables when running a hook. This is useful |
bos@37 | 171 to remember if you are writing a site-wide hook that may be run by a |
bos@37 | 172 number of different users with differing environment variables set. |
bos@37 | 173 In multi-user situations, you should not rely on environment variables |
bos@37 | 174 being set to the values you have in your environment when testing the |
bos@37 | 175 hook. |
bos@37 | 176 |
bos@37 | 177 \subsection{Telling Mercurial to use an in-process hook} |
bos@37 | 178 |
bos@37 | 179 The \hgrc\ syntax for defining an in-process hook is slightly |
bos@37 | 180 different than for an executable hook. The value of the hook must |
bos@37 | 181 start with the text ``\texttt{python:}'', and continue with the |
bos@37 | 182 fully-qualified name of a callable object to use as the hook's value. |
bos@37 | 183 |
bos@37 | 184 The module in which a hook lives is automatically imported when a hook |
bos@37 | 185 is run. So long as you have the module name and \envar{PYTHONPATH} |
bos@37 | 186 right, it should ``just work''. |
bos@37 | 187 |
bos@37 | 188 The following \hgrc\ example snippet illustrates the syntax and |
bos@37 | 189 meaning of the notions we just described. |
bos@37 | 190 \begin{codesample2} |
bos@37 | 191 [hooks] |
bos@37 | 192 commit.example = python:mymodule.submodule.myhook |
bos@37 | 193 \end{codesample2} |
bos@37 | 194 When Mercurial runs the \texttt{commit.example} hook, it imports |
bos@37 | 195 \texttt{mymodule.submodule}, looks for the callable object named |
bos@37 | 196 \texttt{myhook}, and calls it. |
bos@37 | 197 |
bos@37 | 198 \subsection{Writing an in-process hook} |
bos@37 | 199 |
bos@37 | 200 The simplest in-process hook does nothing, but illustrates the basic |
bos@37 | 201 shape of the hook API: |
bos@37 | 202 \begin{codesample2} |
bos@37 | 203 def myhook(ui, repo, **kwargs): |
bos@37 | 204 pass |
bos@37 | 205 \end{codesample2} |
bos@37 | 206 The first argument to a Python hook is always a |
bos@37 | 207 \pymodclass{mercurial.ui}{ui} object. The second is a repository object; |
bos@37 | 208 at the moment, it is always an instance of |
bos@37 | 209 \pymodclass{mercurial.localrepo}{localrepository}. Following these two |
bos@37 | 210 arguments are other keyword arguments. Which ones are passed in |
bos@37 | 211 depends on the hook being called, but a hook can ignore arguments it |
bos@37 | 212 doesn't care about by dropping them into a keyword argument dict, as |
bos@37 | 213 with \texttt{**kwargs} above. |
bos@34 | 214 |
bos@34 | 215 |
bos@34 | 216 %%% Local Variables: |
bos@34 | 217 %%% mode: latex |
bos@34 | 218 %%% TeX-master: "00book" |
bos@34 | 219 %%% End: |