annotate en/hook.tex @ 34:c0979ed1eabd

Get started on hook chapter.
author Bryan O'Sullivan <bos@serpentine.com>
date Sun, 16 Jul 2006 00:01:43 -0700
parents
children 9fd0c59b009a
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
34
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
1 \chapter{Handling repository events with hooks}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
2 \label{chap:hook}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
3
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
4 Mercurial offers a powerful mechanism to let you perform automated
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
5 actions in response to events that occur in a repository. In some
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
6 cases, you can even control Mercurial's response to those events.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
7
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
8 The name Mercurial uses for one of these actions is a \emph{hook}.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
9 Hooks are called ``triggers'' in some revision control systems, but
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
10 the two names refer to the same idea.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
11
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
12 \section{A short tutorial on using hooks}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
13 \label{sec:hook:simple}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
14
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
15 It is easy to write a Mercurial hook. Let's start with a hook that
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
16 runs when you finish a \hgcmd{commit}, and simply prints the hash of
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
17 the changeset you just created. The hook is called \hook{commit}.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
18
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
19 \begin{figure}[ht]
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
20 \interaction{hook.simple.init}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
21 \caption{A simple hook that runs when a changeset is committed}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
22 \label{ex:hook:init}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
23 \end{figure}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
24
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
25 All hooks follow the pattern in example~\ref{ex:hook:init}. You add
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
26 an entry to the \rcsection{hooks} section of your \hgrc\. On the left
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
27 is the name of the event to trigger on; on the right is the action to
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
28 take. As you can see, you can run an arbitrary shell command in a
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
29 hook. Mercurial passes extra information to the hook using
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
30 environment variables (look for \envar{HG\_NODE} in the example).
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
31
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
32 \subsection{Performing multiple actions per event}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
33
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
34 Quite often, you will want to define more than one hook for a
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
35 particular kind of event, as shown in example~\ref{ex:hook:ext}.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
36 Mercurial lets you do this by adding an \emph{extension} to the end of
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
37 a hook's name. You extend a hook's name by giving the name of the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
38 hook, followed by a full stop (the ``\texttt{.}'' character), followed
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
39 by some more text of your choosing. For example, Mercurial will run
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
40 both \texttt{commit.foo} and \texttt{commit.bar} when the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
41 \texttt{commit} event occurs.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
42
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
43 \begin{figure}[ht]
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
44 \interaction{hook.simple.ext}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
45 \caption{Defining a second \hook{commit} hook}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
46 \label{ex:hook:ext}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
47 \end{figure}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
48
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
49 To give a well-defined order of execution when there are multiple
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
50 hooks defined for an event, Mercurial sorts hooks by extension, and
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
51 executes the hook commands in this sorted order. In the above
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
52 example, it will execute \texttt{commit.bar} before
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
53 \texttt{commit.foo}, and \texttt{commit} before both.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
54
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
55 It is a good idea to use a somewhat descriptive extension when you
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
56 define a new hook. This will help you to remember what the hook was
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
57 for. If the hook fails, you'll get an error message that contains the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
58 hook name and extension, so using a descriptive extension could give
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
59 you an immediate hint as to why the hook failed (see
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
60 section~\ref{sec:hook:perm} for an example).
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
61
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
62 \subsection{Controlling whether an activity can proceed}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
63 \label{sec:hook:perm}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
64
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
65 In our earlier examples, we used the \hook{commit} hook, which is
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
66 run after a commit has completed. This is one of several Mercurial
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
67 hooks that run after an activity finishes. Such hooks have no way of
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
68 influencing the activity itself.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
69
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
70 Mercurial defines a number of events that occur before an activity
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
71 starts; or after it starts, but before it finishes. Hooks that
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
72 trigger on these events have the added ability to choose whether the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
73 activity can continue, or will abort.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
74
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
75 The \hook{pretxncommit} hook runs after a commit has all but
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
76 completed. In other words, the metadata representing the changeset
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
77 has been written out to disk, but the transaction has not yet been
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
78 allowed to complete. The \hook{pretxncommit} hook has the ability to
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
79 decide whether the transaction can complete, or must be rolled back.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
80
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
81 If the \hook{pretxncommit} hook exits with a status code of zero, the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
82 transaction is allowed to complete; the commit finishes; and the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
83 \hook{commit} hook is run. If the \hook{pretxncommit} hook exits with
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
84 a non-zero status code, the transaction is rolled back; the metadata
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
85 representing the changeset is erased; and the \hook{commit} hook is
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
86 not run.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
87
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
88 \begin{figure}[ht]
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
89 \interaction{hook.simple.pretxncommit}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
90 \caption{Using the \hook{pretxncommit} hook to control commits}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
91 \label{ex:hook:pretxncommit}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
92 \end{figure}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
93
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
94 The hook in example~\ref{ex:hook:pretxncommit} checks that a commit
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
95 comment contains a bug ID. If it does, the commit can complete. If
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
96 not, the commit is rolled back.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
97
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
98 \section{Choosing how to write a hook}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
99 \label{sec:hook:impl}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
100
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
101 You can write a hook either as a normal program---typically a shell
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
102 script---or as a Python function that is called within the Mercurial
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
103 process.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
104
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
105 Writing a hook as an external program has the advantage that it
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
106 requires no knowledge of Mercurial's internals. You can call normal
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
107 Mercurial commands to get any added information you need. The
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
108 trade-off is that external hooks are slower than in-process hooks.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
109
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
110 An in-process Python hook has complete access to the Mercurial API,
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
111 and does not ``shell out'' to another process, so it is inherently
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
112 faster than an external hook. It is also easier to obtain much of the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
113 information that a hook requires by using the Mercurial API than by
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
114 running Mercurial commands.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
115
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
116 If you are comfortable with Python, or require high performance,
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
117 writing your hooks in Python may be a good choice. However, when you
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
118 have a straightforward hook to write and you don't need to care about
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
119 performance (probably the majority of hooks), a shell script is
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
120 perfectly fine.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
121
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
122 \section{Hook parameters}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
123 \label{sec:hook:param}
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
124
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
125 Mercurial calls each hook with a set of well-defined parameters. In
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
126 Python, a parameter is passed as a keyword argument to your hook
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
127 function. For an external program, a parameter is passed as an
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
128 environment variable.
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
129
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
130 Whether your hook is written in Python or as a shell script, the
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
131 parameter names and values will be the same. A boolean parameter will
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
132 be represented as a boolean value in Python, but as the number 1 (for
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
133 ``true'') or 0 (for ``false'')
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
134
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
135
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
136 %%% Local Variables:
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
137 %%% mode: latex
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
138 %%% TeX-master: "00book"
c0979ed1eabd Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff changeset
139 %%% End: