##// END OF EJS Templates
hooks: sort any dictionaries set in the environment...
Dan Villiom Podlaski Christiansen -
r13207:1775382f default
parent child Browse files
Show More
@@ -1,153 +1,159 b''
1 1 # hook.py - hook support for mercurial
2 2 #
3 3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import os, sys
10 10 import extensions, util
11 11
12 12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
13 13 '''call python hook. hook is callable object, looked up as
14 14 name in python module. if callable returns "true", hook
15 15 fails, else passes. if hook raises exception, treated as
16 16 hook failure. exception propagates if throw is "true".
17 17
18 18 reason for "true" meaning "hook failed" is so that
19 19 unmodified commands (e.g. mercurial.commands.update) can
20 20 be run as hooks without wrappers to convert return values.'''
21 21
22 22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
23 23 obj = funcname
24 24 if not hasattr(obj, '__call__'):
25 25 d = funcname.rfind('.')
26 26 if d == -1:
27 27 raise util.Abort(_('%s hook is invalid ("%s" not in '
28 28 'a module)') % (hname, funcname))
29 29 modname = funcname[:d]
30 30 oldpaths = sys.path
31 31 if hasattr(sys, "frozen"):
32 32 # binary installs require sys.path manipulation
33 33 modpath, modfile = os.path.split(modname)
34 34 if modpath and modfile:
35 35 sys.path = sys.path[:] + [modpath]
36 36 modname = modfile
37 37 try:
38 38 obj = __import__(modname)
39 39 except ImportError:
40 40 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
41 41 try:
42 42 # extensions are loaded with hgext_ prefix
43 43 obj = __import__("hgext_%s" % modname)
44 44 except ImportError:
45 45 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
46 46 if ui.tracebackflag:
47 47 ui.warn(_('exception from first failed import attempt:\n'))
48 48 ui.traceback(e1)
49 49 if ui.tracebackflag:
50 50 ui.warn(_('exception from second failed import attempt:\n'))
51 51 ui.traceback(e2)
52 52 raise util.Abort(_('%s hook is invalid '
53 53 '(import of "%s" failed)') %
54 54 (hname, modname))
55 55 sys.path = oldpaths
56 56 try:
57 57 for p in funcname.split('.')[1:]:
58 58 obj = getattr(obj, p)
59 59 except AttributeError:
60 60 raise util.Abort(_('%s hook is invalid '
61 61 '("%s" is not defined)') %
62 62 (hname, funcname))
63 63 if not hasattr(obj, '__call__'):
64 64 raise util.Abort(_('%s hook is invalid '
65 65 '("%s" is not callable)') %
66 66 (hname, funcname))
67 67 try:
68 68 r = obj(ui=ui, repo=repo, hooktype=name, **args)
69 69 except KeyboardInterrupt:
70 70 raise
71 71 except Exception, exc:
72 72 if isinstance(exc, util.Abort):
73 73 ui.warn(_('error: %s hook failed: %s\n') %
74 74 (hname, exc.args[0]))
75 75 else:
76 76 ui.warn(_('error: %s hook raised an exception: '
77 77 '%s\n') % (hname, exc))
78 78 if throw:
79 79 raise
80 80 ui.traceback()
81 81 return True
82 82 if r:
83 83 if throw:
84 84 raise util.Abort(_('%s hook failed') % hname)
85 85 ui.warn(_('warning: %s hook failed\n') % hname)
86 86 return r
87 87
88 88 def _exthook(ui, repo, name, cmd, args, throw):
89 89 ui.note(_("running hook %s: %s\n") % (name, cmd))
90 90
91 91 env = {}
92 92 for k, v in args.iteritems():
93 93 if hasattr(v, '__call__'):
94 94 v = v()
95 if isinstance(v, dict):
96 # make the dictionary element order stable across Python
97 # implementations
98 v = ('{' +
99 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
100 '}')
95 101 env['HG_' + k.upper()] = v
96 102
97 103 if repo:
98 104 cwd = repo.root
99 105 else:
100 106 cwd = os.getcwd()
101 107 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
102 108 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
103 109 else:
104 110 r = util.system(cmd, environ=env, cwd=cwd)
105 111 if r:
106 112 desc, r = util.explain_exit(r)
107 113 if throw:
108 114 raise util.Abort(_('%s hook %s') % (name, desc))
109 115 ui.warn(_('warning: %s hook %s\n') % (name, desc))
110 116 return r
111 117
112 118 _redirect = False
113 119 def redirect(state):
114 120 global _redirect
115 121 _redirect = state
116 122
117 123 def hook(ui, repo, name, throw=False, **args):
118 124 r = False
119 125
120 126 oldstdout = -1
121 127 if _redirect:
122 128 stdoutno = sys.__stdout__.fileno()
123 129 stderrno = sys.__stderr__.fileno()
124 130 # temporarily redirect stdout to stderr, if possible
125 131 if stdoutno >= 0 and stderrno >= 0:
126 132 oldstdout = os.dup(stdoutno)
127 133 os.dup2(stderrno, stdoutno)
128 134
129 135 try:
130 136 for hname, cmd in ui.configitems('hooks'):
131 137 if hname.split('.')[0] != name or not cmd:
132 138 continue
133 139 if hasattr(cmd, '__call__'):
134 140 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
135 141 elif cmd.startswith('python:'):
136 142 if cmd.count(':') >= 2:
137 143 path, cmd = cmd[7:].rsplit(':', 1)
138 144 path = util.expandpath(path)
139 145 if repo:
140 146 path = os.path.join(repo.root, path)
141 147 mod = extensions.loadpath(path, 'hghook.%s' % hname)
142 148 hookfn = getattr(mod, cmd)
143 149 else:
144 150 hookfn = cmd[7:].strip()
145 151 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
146 152 else:
147 153 r = _exthook(ui, repo, hname, cmd, args, throw) or r
148 154 finally:
149 155 if _redirect and oldstdout >= 0:
150 156 os.dup2(oldstdout, stdoutno)
151 157 os.close(oldstdout)
152 158
153 159 return r
@@ -1,483 +1,483 b''
1 1 $ cp "$TESTDIR"/printenv.py .
2 2
3 3 commit hooks can see env vars
4 4
5 5 $ hg init a
6 6 $ cd a
7 7 $ echo "[hooks]" > .hg/hgrc
8 8 $ echo 'commit = unset HG_LOCAL HG_TAG; python ../printenv.py commit' >> .hg/hgrc
9 9 $ echo 'commit.b = unset HG_LOCAL HG_TAG; python ../printenv.py commit.b' >> .hg/hgrc
10 10 $ echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python ../printenv.py precommit' >> .hg/hgrc
11 11 $ echo 'pretxncommit = unset HG_LOCAL HG_TAG; python ../printenv.py pretxncommit' >> .hg/hgrc
12 12 $ echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
13 13 $ echo 'pre-identify = python ../printenv.py pre-identify 1' >> .hg/hgrc
14 14 $ echo 'pre-cat = python ../printenv.py pre-cat' >> .hg/hgrc
15 15 $ echo 'post-cat = python ../printenv.py post-cat' >> .hg/hgrc
16 16 $ echo a > a
17 17 $ hg add a
18 18 $ hg commit -m a
19 19 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
20 20 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
21 21 0:cb9a9f314b8b
22 22 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
23 23 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
24 24
25 25 $ hg clone . ../b
26 26 updating to branch default
27 27 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 28 $ cd ../b
29 29
30 30 changegroup hooks can see env vars
31 31
32 32 $ echo '[hooks]' > .hg/hgrc
33 33 $ echo 'prechangegroup = python ../printenv.py prechangegroup' >> .hg/hgrc
34 34 $ echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
35 35 $ echo 'incoming = python ../printenv.py incoming' >> .hg/hgrc
36 36
37 37 pretxncommit and commit hooks can see both parents of merge
38 38
39 39 $ cd ../a
40 40 $ echo b >> a
41 41 $ hg commit -m a1 -d "1 0"
42 42 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
43 43 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
44 44 1:ab228980c14d
45 45 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
46 46 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
47 47 $ hg update -C 0
48 48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 49 $ echo b > b
50 50 $ hg add b
51 51 $ hg commit -m b -d '1 0'
52 52 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
53 53 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
54 54 2:ee9deb46ab31
55 55 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
56 56 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
57 57 created new head
58 58 $ hg merge 1
59 59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 60 (branch merge, don't forget to commit)
61 61 $ hg commit -m merge -d '2 0'
62 62 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
63 63 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
64 64 3:07f3376c1e65
65 65 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
66 66 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
67 67
68 68 test generic hooks
69 69
70 70 $ hg id
71 pre-identify hook: HG_ARGS=id HG_OPTS={'tags': None, 'rev': '', 'num': None, 'branch': None, 'id': None} HG_PATS=[]
71 pre-identify hook: HG_ARGS=id HG_OPTS={'branch': None, 'id': None, 'num': None, 'rev': '', 'tags': None} HG_PATS=[]
72 72 warning: pre-identify hook exited with status 1
73 73 [1]
74 74 $ hg cat b
75 pre-cat hook: HG_ARGS=cat b HG_OPTS={'rev': '', 'decode': None, 'exclude': [], 'output': '', 'include': []} HG_PATS=['b']
75 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
76 76 b
77 post-cat hook: HG_ARGS=cat b HG_OPTS={'rev': '', 'decode': None, 'exclude': [], 'output': '', 'include': []} HG_PATS=['b'] HG_RESULT=0
77 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
78 78
79 79 $ cd ../b
80 80 $ hg pull ../a
81 81 prechangegroup hook: HG_SOURCE=pull HG_URL=file:
82 82 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:
83 83 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:
84 84 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:
85 85 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:
86 86 pulling from ../a
87 87 searching for changes
88 88 adding changesets
89 89 adding manifests
90 90 adding file changes
91 91 added 3 changesets with 2 changes to 2 files
92 92 (run 'hg update' to get a working copy)
93 93
94 94 tag hooks can see env vars
95 95
96 96 $ cd ../a
97 97 $ echo 'pretag = python ../printenv.py pretag' >> .hg/hgrc
98 98 $ echo 'tag = unset HG_PARENT1 HG_PARENT2; python ../printenv.py tag' >> .hg/hgrc
99 99 $ hg tag -d '3 0' a
100 100 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
101 101 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
102 102 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
103 103 4:539e4b31b6dc
104 104 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
105 105 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
106 106 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
107 107 $ hg tag -l la
108 108 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
109 109 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
110 110
111 111 pretag hook can forbid tagging
112 112
113 113 $ echo 'pretag.forbid = python ../printenv.py pretag.forbid 1' >> .hg/hgrc
114 114 $ hg tag -d '4 0' fa
115 115 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
116 116 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
117 117 abort: pretag.forbid hook exited with status 1
118 118 [255]
119 119 $ hg tag -l fla
120 120 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
121 121 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
122 122 abort: pretag.forbid hook exited with status 1
123 123 [255]
124 124
125 125 pretxncommit hook can see changeset, can roll back txn, changeset no
126 126 more there after
127 127
128 128 $ echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
129 129 $ echo 'pretxncommit.forbid1 = python ../printenv.py pretxncommit.forbid 1' >> .hg/hgrc
130 130 $ echo z > z
131 131 $ hg add z
132 132 $ hg -q tip
133 133 4:539e4b31b6dc
134 134 $ hg commit -m 'fail' -d '4 0'
135 135 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
136 136 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
137 137 5:6f611f8018c1
138 138 5:6f611f8018c1
139 139 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
140 140 transaction abort!
141 141 rollback completed
142 142 abort: pretxncommit.forbid1 hook exited with status 1
143 143 [255]
144 144 $ hg -q tip
145 145 4:539e4b31b6dc
146 146
147 147 precommit hook can prevent commit
148 148
149 149 $ echo 'precommit.forbid = python ../printenv.py precommit.forbid 1' >> .hg/hgrc
150 150 $ hg commit -m 'fail' -d '4 0'
151 151 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
152 152 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
153 153 abort: precommit.forbid hook exited with status 1
154 154 [255]
155 155 $ hg -q tip
156 156 4:539e4b31b6dc
157 157
158 158 preupdate hook can prevent update
159 159
160 160 $ echo 'preupdate = python ../printenv.py preupdate' >> .hg/hgrc
161 161 $ hg update 1
162 162 preupdate hook: HG_PARENT1=ab228980c14d
163 163 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
164 164
165 165 update hook
166 166
167 167 $ echo 'update = python ../printenv.py update' >> .hg/hgrc
168 168 $ hg update
169 169 preupdate hook: HG_PARENT1=539e4b31b6dc
170 170 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
171 171 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
172 172
173 173 prechangegroup hook can prevent incoming changes
174 174
175 175 $ cd ../b
176 176 $ hg -q tip
177 177 3:07f3376c1e65
178 178 $ echo '[hooks]' > .hg/hgrc
179 179 $ echo 'prechangegroup.forbid = python ../printenv.py prechangegroup.forbid 1' >> .hg/hgrc
180 180 $ hg pull ../a
181 181 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:
182 182 pulling from ../a
183 183 searching for changes
184 184 abort: prechangegroup.forbid hook exited with status 1
185 185 [255]
186 186
187 187 pretxnchangegroup hook can see incoming changes, can roll back txn,
188 188 incoming changes no longer there after
189 189
190 190 $ echo '[hooks]' > .hg/hgrc
191 191 $ echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
192 192 $ echo 'pretxnchangegroup.forbid1 = python ../printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
193 193 $ hg pull ../a
194 194 4:539e4b31b6dc
195 195 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:
196 196 pulling from ../a
197 197 searching for changes
198 198 adding changesets
199 199 adding manifests
200 200 adding file changes
201 201 added 1 changesets with 1 changes to 1 files
202 202 transaction abort!
203 203 rollback completed
204 204 abort: pretxnchangegroup.forbid1 hook exited with status 1
205 205 [255]
206 206 $ hg -q tip
207 207 3:07f3376c1e65
208 208
209 209 outgoing hooks can see env vars
210 210
211 211 $ rm .hg/hgrc
212 212 $ echo '[hooks]' > ../a/.hg/hgrc
213 213 $ echo 'preoutgoing = python ../printenv.py preoutgoing' >> ../a/.hg/hgrc
214 214 $ echo 'outgoing = python ../printenv.py outgoing' >> ../a/.hg/hgrc
215 215 $ hg pull ../a
216 216 preoutgoing hook: HG_SOURCE=pull
217 217 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
218 218 pulling from ../a
219 219 searching for changes
220 220 adding changesets
221 221 adding manifests
222 222 adding file changes
223 223 added 1 changesets with 1 changes to 1 files
224 224 (run 'hg update' to get a working copy)
225 225 $ hg rollback
226 226 rolling back to revision 3 (undo pull)
227 227
228 228 preoutgoing hook can prevent outgoing changes
229 229
230 230 $ echo 'preoutgoing.forbid = python ../printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
231 231 $ hg pull ../a
232 232 preoutgoing hook: HG_SOURCE=pull
233 233 preoutgoing.forbid hook: HG_SOURCE=pull
234 234 pulling from ../a
235 235 searching for changes
236 236 abort: preoutgoing.forbid hook exited with status 1
237 237 [255]
238 238
239 239 outgoing hooks work for local clones
240 240
241 241 $ cd ..
242 242 $ echo '[hooks]' > a/.hg/hgrc
243 243 $ echo 'preoutgoing = python ../printenv.py preoutgoing' >> a/.hg/hgrc
244 244 $ echo 'outgoing = python ../printenv.py outgoing' >> a/.hg/hgrc
245 245 $ hg clone a c
246 246 preoutgoing hook: HG_SOURCE=clone
247 247 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
248 248 updating to branch default
249 249 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 250 $ rm -rf c
251 251
252 252 preoutgoing hook can prevent outgoing changes for local clones
253 253
254 254 $ echo 'preoutgoing.forbid = python ../printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
255 255 $ hg clone a zzz
256 256 preoutgoing hook: HG_SOURCE=clone
257 257 preoutgoing.forbid hook: HG_SOURCE=clone
258 258 abort: preoutgoing.forbid hook exited with status 1
259 259 [255]
260 260 $ cd b
261 261
262 262 $ cat > hooktests.py <<EOF
263 263 > from mercurial import util
264 264 >
265 265 > uncallable = 0
266 266 >
267 267 > def printargs(args):
268 268 > args.pop('ui', None)
269 269 > args.pop('repo', None)
270 270 > a = list(args.items())
271 271 > a.sort()
272 272 > print 'hook args:'
273 273 > for k, v in a:
274 274 > print ' ', k, v
275 275 >
276 276 > def passhook(**args):
277 277 > printargs(args)
278 278 >
279 279 > def failhook(**args):
280 280 > printargs(args)
281 281 > return True
282 282 >
283 283 > class LocalException(Exception):
284 284 > pass
285 285 >
286 286 > def raisehook(**args):
287 287 > raise LocalException('exception from hook')
288 288 >
289 289 > def aborthook(**args):
290 290 > raise util.Abort('raise abort from hook')
291 291 >
292 292 > def brokenhook(**args):
293 293 > return 1 + {}
294 294 >
295 295 > class container:
296 296 > unreachable = 1
297 297 > EOF
298 298
299 299 test python hooks
300 300
301 301 $ PYTHONPATH="`pwd`:$PYTHONPATH"
302 302 $ export PYTHONPATH
303 303
304 304 $ echo '[hooks]' > ../a/.hg/hgrc
305 305 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
306 306 $ hg pull ../a 2>&1 | grep 'raised an exception'
307 307 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
308 308
309 309 $ echo '[hooks]' > ../a/.hg/hgrc
310 310 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
311 311 $ hg pull ../a 2>&1 | grep 'raised an exception'
312 312 error: preoutgoing.raise hook raised an exception: exception from hook
313 313
314 314 $ echo '[hooks]' > ../a/.hg/hgrc
315 315 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
316 316 $ hg pull ../a
317 317 pulling from ../a
318 318 searching for changes
319 319 error: preoutgoing.abort hook failed: raise abort from hook
320 320 abort: raise abort from hook
321 321 [255]
322 322
323 323 $ echo '[hooks]' > ../a/.hg/hgrc
324 324 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
325 325 $ hg pull ../a
326 326 pulling from ../a
327 327 searching for changes
328 328 hook args:
329 329 hooktype preoutgoing
330 330 source pull
331 331 abort: preoutgoing.fail hook failed
332 332 [255]
333 333
334 334 $ echo '[hooks]' > ../a/.hg/hgrc
335 335 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
336 336 $ hg pull ../a
337 337 pulling from ../a
338 338 searching for changes
339 339 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
340 340 [255]
341 341
342 342 $ echo '[hooks]' > ../a/.hg/hgrc
343 343 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
344 344 $ hg pull ../a
345 345 pulling from ../a
346 346 searching for changes
347 347 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
348 348 [255]
349 349
350 350 $ echo '[hooks]' > ../a/.hg/hgrc
351 351 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
352 352 $ hg pull ../a
353 353 pulling from ../a
354 354 searching for changes
355 355 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
356 356 [255]
357 357
358 358 $ echo '[hooks]' > ../a/.hg/hgrc
359 359 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
360 360 $ hg pull ../a
361 361 pulling from ../a
362 362 searching for changes
363 363 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
364 364 [255]
365 365
366 366 $ echo '[hooks]' > ../a/.hg/hgrc
367 367 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
368 368 $ hg pull ../a
369 369 pulling from ../a
370 370 searching for changes
371 371 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
372 372 [255]
373 373
374 374 $ echo '[hooks]' > ../a/.hg/hgrc
375 375 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
376 376 $ hg pull ../a
377 377 pulling from ../a
378 378 searching for changes
379 379 hook args:
380 380 hooktype preoutgoing
381 381 source pull
382 382 adding changesets
383 383 adding manifests
384 384 adding file changes
385 385 added 1 changesets with 1 changes to 1 files
386 386 (run 'hg update' to get a working copy)
387 387
388 388 make sure --traceback works
389 389
390 390 $ echo '[hooks]' > .hg/hgrc
391 391 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
392 392
393 393 $ echo aa > a
394 394 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
395 395 Traceback (most recent call last):
396 396
397 397 $ cd ..
398 398 $ hg init c
399 399 $ cd c
400 400
401 401 $ cat > hookext.py <<EOF
402 402 > def autohook(**args):
403 403 > print "Automatically installed hook"
404 404 >
405 405 > def reposetup(ui, repo):
406 406 > repo.ui.setconfig("hooks", "commit.auto", autohook)
407 407 > EOF
408 408 $ echo '[extensions]' >> .hg/hgrc
409 409 $ echo 'hookext = hookext.py' >> .hg/hgrc
410 410
411 411 $ touch foo
412 412 $ hg add foo
413 413 $ hg ci -d '0 0' -m 'add foo'
414 414 Automatically installed hook
415 415 $ echo >> foo
416 416 $ hg ci --debug -d '0 0' -m 'change foo'
417 417 foo
418 418 calling hook commit.auto: <function autohook at *> (glob)
419 419 Automatically installed hook
420 420 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
421 421
422 422 $ hg showconfig hooks
423 423 hooks.commit.auto=<function autohook at *> (glob)
424 424
425 425 test python hook configured with python:[file]:[hook] syntax
426 426
427 427 $ cd ..
428 428 $ mkdir d
429 429 $ cd d
430 430 $ hg init repo
431 431 $ mkdir hooks
432 432
433 433 $ cd hooks
434 434 $ cat > testhooks.py <<EOF
435 435 > def testhook(**args):
436 436 > print 'hook works'
437 437 > EOF
438 438 $ echo '[hooks]' > ../repo/.hg/hgrc
439 439 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
440 440
441 441 $ cd ../repo
442 442 $ hg commit -d '0 0'
443 443 hook works
444 444 nothing changed
445 445 [1]
446 446
447 447 $ cd ../../b
448 448
449 449 make sure --traceback works on hook import failure
450 450
451 451 $ cat > importfail.py <<EOF
452 452 > import somebogusmodule
453 453 > # dereference something in the module to force demandimport to load it
454 454 > somebogusmodule.whatever
455 455 > EOF
456 456
457 457 $ echo '[hooks]' > .hg/hgrc
458 458 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
459 459
460 460 $ echo a >> a
461 461 $ hg --traceback commit -ma 2>&1 | egrep '^(exception|Traceback|ImportError)'
462 462 exception from first failed import attempt:
463 463 Traceback (most recent call last):
464 464 ImportError: No module named somebogusmodule
465 465 exception from second failed import attempt:
466 466 Traceback (most recent call last):
467 467 ImportError: No module named hgext_importfail
468 468 Traceback (most recent call last):
469 469
470 470 Issue1827: Hooks Update & Commit not completely post operation
471 471
472 472 commit and update hooks should run after command completion
473 473
474 474 $ echo '[hooks]' > .hg/hgrc
475 475 $ echo 'commit = hg id' >> .hg/hgrc
476 476 $ echo 'update = hg id' >> .hg/hgrc
477 477 $ echo bb > a
478 478 $ hg ci -ma
479 479 223eafe2750c tip
480 480 $ hg up 0
481 481 cb9a9f314b8b
482 482 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
483 483
General Comments 0
You need to be logged in to leave comments. Login now