Mercurial > hgbook
annotate en/hook.tex @ 39:576fef93bb49
Further content for hook chapter.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Wed, 19 Jul 2006 16:32:10 -0700 |
parents | b49a7dd4e564 |
children | b2fe9964b21b |
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 |
38
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
12 \section{An overview of hooks in Mercurial} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
13 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
14 Here is a brief list of the hooks that Mercurial supports. For each |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
15 hook, we indicate when it is run, and a few examples of common tasks |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
16 you can use it for. We will revisit each of these hooks in more |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
17 detail later. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
18 \begin{itemize} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
19 \item[\small\hook{changegroup}] This is run after a group of |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
20 changesets has been brought into the repository from elsewhere. In |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
21 other words, it is run after a \hgcmd{pull} or \hgcmd{push} into a |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
22 repository, but not after a \hgcmd{commit}. You can use this for |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
23 performing an action once for the entire group of newly arrived |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
24 changesets. For example, you could use this hook to send out email |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
25 notifications, or kick off an automated build or test. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
26 \item[\small\hook{commit}] This is run after a new changeset has been |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
27 created in the local repository, typically using the \hgcmd{commit} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
28 command. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
29 \item[\small\hook{incoming}] This is run once for each new changeset |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
30 that is brought into the repository from elsewhere. Notice the |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
31 difference from \hook{changegroup}, which is run once per |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
32 \emph{group} of changesets brought in. You can use this for the |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
33 same purposes as the \hook{changegroup} hook; it's simply more |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
34 convenient sometimes to run a hook once per group of changesets, |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
35 while othher times it's handier once per changeset. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
36 \item[\small\hook{outgoing}] This is run after a group of changesets |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
37 has been transmitted from this repository to another. You can use |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
38 this, for example, to notify subscribers every time changes are |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
39 cloned or pulled from the repository. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
40 \item[\small\hook{prechangegroup}] This is run before starting to |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
41 bring a group of changesets into the repository. It cannot see the |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
42 actual changesets, because they have not yet been transmitted. If |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
43 it fails, the changesets will not be transmitted. You can use this |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
44 hook to ``lock down'' a repository against incoming changes. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
45 \item[\small\hook{precommit}] This is run before starting a commit. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
46 It cannot tell what files are included in the commit, or any other |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
47 information about the commit. If it fails, the commit will not be |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
48 allowed to start. You can use this to perform a build and require |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
49 it to complete successfully before a commit can proceed, or |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
50 automatically enforce a requirement that modified files pass your |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
51 coding style guidelines. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
52 \item[\small\hook{preoutgoing}] This is run before starting to |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
53 transmit a group of changesets from this repository. You can use |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
54 this to lock a repository against clones or pulls from remote |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
55 clients. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
56 \item[\small\hook{pretag}] This is run before creating a tag. If it |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
57 fails, the tag will not be created. You can use this to enforce a |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
58 uniform tag naming convention. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
59 \item[\small\hook{pretxnchangegroup}] This is run after a group of |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
60 changesets has been brought into the local repository from another, |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
61 but before the transaction completes that will make the changes |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
62 permanent in the repository. If it fails, the transaction will be |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
63 rolled back and the changes will disappear from the local |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
64 repository. You can use this to automatically check newly arrived |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
65 changes and, for example, roll them back if the group as a whole |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
66 does not build or pass your test suite. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
67 \item[\small\hook{pretxncommit}] This is run after a new changeset has |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
68 been created in the local repository, but before the transaction |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
69 completes that will make it permanent. Unlike the \hook{precommit} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
70 hook, this hook can see which changes are present in the changeset, |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
71 and it can also see all other changeset metadata, such as the commit |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
72 message. You can use this to require that a commit message follows |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
73 your local conventions, or that a changeset builds cleanly. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
74 \item[\small\hook{preupdate}] This is run before starting an update or |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
75 merge of the working directory. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
76 \item[\small\hook{tag}] This is run after a tag is created. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
77 \item[\small\hook{update}] This is run after an update or merge of the |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
78 working directory has finished. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
79 \end{itemize} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
80 Each of the hooks with a ``\texttt{pre}'' prefix has the ability to |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
81 \emph{control} an activity. If the hook succeeds, the activity may |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
82 proceed; if it fails, the activity is either not permitted or undone, |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
83 depending on the hook. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
84 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
85 \section{Hooks and security} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
86 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
87 \subsection{Hooks are run with your privileges} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
88 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
89 When you run a Mercurial command in a repository, and the command |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
90 causes a hook to run, that hook runs on your system, under your user |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
91 account, with your privilege level. Since hooks are arbitrary pieces |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
92 of executable code, you should treat them with an appropriate level of |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
93 suspicion. Do not install a hook unless you are confident that you |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
94 know who created it and what it does. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
95 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
96 In some cases, you may be exposed to hooks that you did not install |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
97 yourself. If you work with Mercurial on an unfamiliar system, |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
98 Mercurial will run hooks defined in that system's global \hgrc\ file. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
99 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
100 If you are working with a repository owned by another user, Mercurial |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
101 will run hooks defined in that repository. For example, if you |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
102 \hgcmd{pull} from that repository, and its \sfilename{.hg/hgrc} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
103 defines a local \hook{outgoing} hook, that hook will run under your |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
104 user account, even though you don't own that repository. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
105 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
106 \begin{note} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
107 This only applies if you are pulling from a repository on a local or |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
108 network filesystem. If you're pulling over http or ssh, any |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
109 \hook{outgoing} hook will run under the account of the server |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
110 process, on the server. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
111 \end{note} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
112 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
113 XXX To see what hooks are defined in a repository, use the |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
114 \hgcmdargs{config}{hooks} command. If you are working in one |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
115 repository, but talking to another that you do not own (e.g.~using |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
116 \hgcmd{pull} or \hgcmd{incoming}), remember that it is the other |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
117 repository's hooks you should be checking, not your own. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
118 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
119 \subsection{Hooks do not propagate} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
120 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
121 In Mercurial, hooks are not revision controlled, and do not propagate |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
122 when you clone, or pull from, a repository. The reason for this is |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
123 simple: a hook is a completely arbitrary piece of executable code. It |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
124 runs under your user identity, with your privilege level, on your |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
125 machine. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
126 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
127 It would be extremely reckless for any distributed revision control |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
128 system to implement revision-controlled hooks, as this would offer an |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
129 easily exploitable way to subvert the accounts of users of the |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
130 revision control system. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
131 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
132 Since Mercurial does not propagate hooks, if you are collaborating |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
133 with other people on a common project, you should not assume that they |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
134 are using the same Mercurial hooks as you are, or that theirs are |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
135 correctly configured. You should document the hooks you expect people |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
136 to use. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
137 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
138 In a corporate intranet, this is somewhat easier to control, as you |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
139 can for example provide a ``standard'' installation of Mercurial on an |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
140 NFS filesystem, and use a site-wide \hgrc\ file to define hooks that |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
141 all users will see. However, this too has its limits; see below. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
142 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
143 \subsection{Hooks can be overridden} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
144 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
145 Mercurial allows you to override a hook definition by redefining the |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
146 hook. You can disable it by setting its value to the empty string, or |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
147 change its behaviour as you wish. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
148 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
149 If you deploy a system-~or site-wide \hgrc\ file that defines some |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
150 hooks, you should thus understand that your users can disable or |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
151 override those hooks. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
152 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
153 \subsection{Ensuring that critical hooks are run} |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
154 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
155 Sometimes you may want to enforce a policy that you do not want others |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
156 to be able to work around. For example, you may have a requirement |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
157 that every changeset must pass a rigorous set of tests. Defining this |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
158 requirement via a hook in a site-wide \hgrc\ won't work for remote |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
159 users on laptops, and of course local users can subvert it at will by |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
160 overriding the hook. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
161 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
162 Instead, you can set up your policies for use of Mercurial so that |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
163 people are expected to propagate changes through a well-known |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
164 ``canonical'' server that you have locked down and configured |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
165 appropriately. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
166 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
167 One way to do this is via a combination of social engineering and |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
168 technology. Set up a restricted-access account; users can push |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
169 changes over the network to repositories managed by this account, but |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
170 they cannot log into the account and run normal shell commands. In |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
171 this scenario, a user can commit a changeset that contains any old |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
172 garbage they want. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
173 |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
174 When someone pushes a changeset to the server that everyone pulls |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
175 from, the server will test the changeset before it accepts it as |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
176 permanent, and reject it if it fails to pass the test suite. If |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
177 people only pull changes from this filtering server, it will serve to |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
178 ensure that all changes that people pull have been automatically |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
179 vetted. |
b49a7dd4e564
More content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
37
diff
changeset
|
180 |
34
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
181 \section{A short tutorial on using hooks} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
182 \label{sec:hook:simple} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
183 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
184 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
|
185 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
|
186 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
|
187 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
188 \begin{figure}[ht] |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
189 \interaction{hook.simple.init} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
190 \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
|
191 \label{ex:hook:init} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
192 \end{figure} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
193 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
194 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
|
195 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
|
196 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
|
197 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
|
198 hook. Mercurial passes extra information to the hook using |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
199 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
|
200 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
201 \subsection{Performing multiple actions per event} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
202 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
203 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
|
204 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
|
205 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
|
206 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
|
207 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
|
208 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
|
209 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
|
210 \texttt{commit} event occurs. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
211 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
212 \begin{figure}[ht] |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
213 \interaction{hook.simple.ext} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
214 \caption{Defining a second \hook{commit} hook} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
215 \label{ex:hook:ext} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
216 \end{figure} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
217 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
218 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
|
219 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
|
220 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
|
221 example, it will execute \texttt{commit.bar} before |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
222 \texttt{commit.foo}, and \texttt{commit} before both. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
223 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
224 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
|
225 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
|
226 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
|
227 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
|
228 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
|
229 section~\ref{sec:hook:perm} for an example). |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
230 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
231 \subsection{Controlling whether an activity can proceed} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
232 \label{sec:hook:perm} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
233 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
234 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
|
235 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
|
236 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
|
237 influencing the activity itself. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
238 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
239 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
|
240 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
|
241 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
|
242 activity can continue, or will abort. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
243 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
244 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
|
245 completed. In other words, the metadata representing the changeset |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
246 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
|
247 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
|
248 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
|
249 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
250 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
|
251 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
|
252 \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
|
253 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
|
254 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
|
255 not run. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
256 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
257 \begin{figure}[ht] |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
258 \interaction{hook.simple.pretxncommit} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
259 \caption{Using the \hook{pretxncommit} hook to control commits} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
260 \label{ex:hook:pretxncommit} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
261 \end{figure} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
262 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
263 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
|
264 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
|
265 not, the commit is rolled back. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
266 |
37 | 267 \section{Writing your own hooks} |
268 | |
269 When you are writing a hook, you might find it useful to run Mercurial | |
270 either with the \hggopt{-v} option, or the \rcitem{ui}{verbose} config | |
271 item set to ``true''. When you do so, Mercurial will print a message | |
272 before it calls each hook. | |
273 | |
274 \subsection{Choosing how your hook should run} | |
275 \label{sec:hook:lang} | |
34
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
276 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
277 You can write a hook either as a normal program---typically a shell |
37 | 278 script---or as a Python function that is executed within the Mercurial |
34
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
279 process. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
280 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
281 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
|
282 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
|
283 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
|
284 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
|
285 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
286 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
|
287 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
|
288 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
|
289 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
|
290 running Mercurial commands. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
291 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
292 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
|
293 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
|
294 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
|
295 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
|
296 perfectly fine. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
297 |
37 | 298 \subsection{Hook parameters} |
34
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
299 \label{sec:hook:param} |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
300 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
301 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
|
302 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
|
303 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
|
304 environment variable. |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
305 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
306 Whether your hook is written in Python or as a shell script, the |
37 | 307 hook-specific parameter names and values will be the same. A boolean |
308 parameter will be represented as a boolean value in Python, but as the | |
309 number 1 (for ``true'') or 0 (for ``false'') as an environment | |
310 variable for an external hook. If a hook parameter is named | |
311 \texttt{foo}, the keyword argument for a Python hook will also be | |
312 named \texttt{foo} Python, while the environment variable for an | |
313 external hook will be named \texttt{HG\_FOO}. | |
314 | |
315 \subsection{Hook return values and activity control} | |
316 | |
317 A hook that executes successfully must exit with a status of zero if | |
318 external, or return boolean ``false'' if in-process. Failure is | |
319 indicated with a non-zero exit status from an external hook, or an | |
320 in-process hook returning boolean ``true''. If an in-process hook | |
321 raises an exception, the hook is considered to have failed. | |
322 | |
323 For a hook that controls whether an activity can proceed, zero/false | |
324 means ``allow'', while non-zero/true/exception means ``deny''. | |
325 | |
326 \subsection{Writing an external hook} | |
327 | |
328 When you define an external hook in your \hgrc\ and the hook is run, | |
329 its value is passed to your shell, which interprets it. This means | |
330 that you can use normal shell constructs in the body of the hook. | |
331 | |
332 An executable hook is always run with its current directory set to a | |
333 repository's root directory. | |
334 | |
335 Each hook parameter is passed in as an environment variable; the name | |
336 is upper-cased, and prefixed with the string ``\texttt{HG\_}''. | |
337 | |
338 With the exception of hook parameters, Mercurial does not set or | |
339 modify any environment variables when running a hook. This is useful | |
340 to remember if you are writing a site-wide hook that may be run by a | |
341 number of different users with differing environment variables set. | |
342 In multi-user situations, you should not rely on environment variables | |
343 being set to the values you have in your environment when testing the | |
344 hook. | |
345 | |
346 \subsection{Telling Mercurial to use an in-process hook} | |
347 | |
348 The \hgrc\ syntax for defining an in-process hook is slightly | |
349 different than for an executable hook. The value of the hook must | |
350 start with the text ``\texttt{python:}'', and continue with the | |
351 fully-qualified name of a callable object to use as the hook's value. | |
352 | |
353 The module in which a hook lives is automatically imported when a hook | |
354 is run. So long as you have the module name and \envar{PYTHONPATH} | |
355 right, it should ``just work''. | |
356 | |
357 The following \hgrc\ example snippet illustrates the syntax and | |
358 meaning of the notions we just described. | |
359 \begin{codesample2} | |
360 [hooks] | |
361 commit.example = python:mymodule.submodule.myhook | |
362 \end{codesample2} | |
363 When Mercurial runs the \texttt{commit.example} hook, it imports | |
364 \texttt{mymodule.submodule}, looks for the callable object named | |
365 \texttt{myhook}, and calls it. | |
366 | |
367 \subsection{Writing an in-process hook} | |
368 | |
369 The simplest in-process hook does nothing, but illustrates the basic | |
370 shape of the hook API: | |
371 \begin{codesample2} | |
372 def myhook(ui, repo, **kwargs): | |
373 pass | |
374 \end{codesample2} | |
375 The first argument to a Python hook is always a | |
376 \pymodclass{mercurial.ui}{ui} object. The second is a repository object; | |
377 at the moment, it is always an instance of | |
378 \pymodclass{mercurial.localrepo}{localrepository}. Following these two | |
379 arguments are other keyword arguments. Which ones are passed in | |
380 depends on the hook being called, but a hook can ignore arguments it | |
381 doesn't care about by dropping them into a keyword argument dict, as | |
382 with \texttt{**kwargs} above. | |
34
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
383 |
39
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
384 \section{Hook reference} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
385 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
386 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
387 \subsection{In-process hook execution} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
388 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
389 An in-process hook is called with arguments of the following form: |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
390 \begin{codesample2} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
391 def myhook(ui, repo, **kwargs): |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
392 pass |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
393 \end{codesample2} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
394 The \texttt{ui} parameter is a \pymodclass{mercurial.ui}{ui} object. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
395 The \texttt{repo} parameter is a |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
396 \pymodclass{mercurial.localrepo}{localrepository} object. The |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
397 names and values of the \texttt{**kwargs} parameters depend on the |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
398 hook being invoked, with the following common features: |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
399 \begin{itemize} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
400 \item If a parameter is named \texttt{node} or |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
401 \texttt{parent\emph{N}}, it will contain a hexadecimal changeset ID. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
402 The empty string is used to represent ``null changeset ID'' instead |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
403 of a string of zeroes. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
404 \item Boolean-valued parameters are represented as Python |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
405 \texttt{bool} objects. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
406 \end{itemize} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
407 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
408 An in-process hook is called without a change to the process's working |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
409 directory (unlike external hooks, which are run in the root of the |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
410 repository). It must not change the process's working directory. If |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
411 it were to do so, it would probably cause calls to the Mercurial API, |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
412 or operations after the hook finishes, to fail. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
413 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
414 If a hook returns a boolean ``false'' value, it is considered to |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
415 have succeeded. If it returns a boolean ``true'' value or raises an |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
416 exception, it is considered to have failed. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
417 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
418 \subsection{External hook execution} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
419 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
420 An external hook is passed to the user's shell for execution, so |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
421 features of that shell, such as variable substitution and command |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
422 redirection, are available. The hook is run in the root directory of |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
423 the repository. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
424 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
425 Hook parameters are passed to the hook as environment variables. Each |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
426 environment variable's name is converted in upper case and prefixed |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
427 with the string ``\texttt{HG\_}''. For example, if the name of a |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
428 parameter is ``\texttt{node}'', the name of the environment variable |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
429 representing that parameter will be ``\texttt{HG\_NODE}''. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
430 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
431 A boolean parameter is represented as the string ``\texttt{1}'' for |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
432 ``true'', ``\texttt{0}'' for ``false''. If an environment variable is |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
433 named \envar{HG\_NODE}, \envar{HG\_PARENT1} or \envar{HG\_PARENT2}, it |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
434 contains a changeset ID represented as a hexadecimal string. The |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
435 empty string is used to represent ``null changeset ID'' instead of a |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
436 string of zeroes. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
437 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
438 If a hook exits with a status of zero, it is considered to have |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
439 succeeded. If it exits with a non-zero status, it is considered to |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
440 have failed. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
441 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
442 \subsection{The \hook{changegroup} hook} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
443 \label{sec:hook:changegroup} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
444 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
445 Parameters to this hook: |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
446 \begin{itemize} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
447 \item[\texttt{node}] The changeset ID of the first changeset in the |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
448 group that was added. All changesets between this and |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
449 \index{tags!\texttt{tip}}\texttt{tip}, inclusive, were added by a |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
450 single \hgcmd{pull}, \hgcmd{push} or \hgcmd{unbundle}. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
451 \end{itemize} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
452 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
453 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
454 \subsection{The \hook{commit} hook} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
455 \label{sec:hook:commit} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
456 |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
457 Parameters to this hook: |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
458 \begin{itemize} |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
459 \item[\texttt{node}] The changeset ID of the newly committed |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
460 changeset. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
461 \item[\texttt{parent1}] The changeset ID of the first parent of the |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
462 newly committed changeset. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
463 \item[\texttt{parent2}] The changeset ID of the second parent of the |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
464 newly committed changeset. |
576fef93bb49
Further content for hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
38
diff
changeset
|
465 \end{itemize} |
34
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
466 |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
467 %%% Local Variables: |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
468 %%% mode: latex |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
469 %%% TeX-master: "00book" |
c0979ed1eabd
Get started on hook chapter.
Bryan O'Sullivan <bos@serpentine.com>
parents:
diff
changeset
|
470 %%% End: |