##// END OF EJS Templates
hook: raise a separate exception for when loading a hook fails...
Siddharth Agarwal -
r26692:8d1cfd77 default
parent child Browse files
Show More
@@ -1,215 +1,221 b''
1 1 # error.py - Mercurial exceptions
2 2 #
3 3 # Copyright 2005-2008 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 """Mercurial exceptions.
9 9
10 10 This allows us to catch exceptions at higher levels without forcing
11 11 imports.
12 12 """
13 13
14 14 from __future__ import absolute_import
15 15
16 16 # Do not import anything here, please
17 17
18 18 class HintException(Exception):
19 19 def __init__(self, *args, **kw):
20 20 Exception.__init__(self, *args)
21 21 self.hint = kw.get('hint')
22 22
23 23 class RevlogError(HintException):
24 24 pass
25 25
26 26 class FilteredIndexError(IndexError):
27 27 pass
28 28
29 29 class LookupError(RevlogError, KeyError):
30 30 def __init__(self, name, index, message):
31 31 self.name = name
32 32 self.index = index
33 33 # this can't be called 'message' because at least some installs of
34 34 # Python 2.6+ complain about the 'message' property being deprecated
35 35 self.lookupmessage = message
36 36 if isinstance(name, str) and len(name) == 20:
37 37 from .node import short
38 38 name = short(name)
39 39 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
40 40
41 41 def __str__(self):
42 42 return RevlogError.__str__(self)
43 43
44 44 class FilteredLookupError(LookupError):
45 45 pass
46 46
47 47 class ManifestLookupError(LookupError):
48 48 pass
49 49
50 50 class CommandError(Exception):
51 51 """Exception raised on errors in parsing the command line."""
52 52
53 53 class InterventionRequired(Exception):
54 54 """Exception raised when a command requires human intervention."""
55 55
56 56 class Abort(HintException):
57 57 """Raised if a command needs to print an error and exit."""
58 58 pass
59 59
60 class HookLoadError(Abort):
61 """raised when loading a hook fails, aborting an operation
62
63 Exists to allow more specialized catching."""
64 pass
65
60 66 class HookAbort(Abort):
61 67 """raised when a validation hook fails, aborting an operation
62 68
63 69 Exists to allow more specialized catching."""
64 70 pass
65 71
66 72 class ConfigError(Abort):
67 73 """Exception raised when parsing config files"""
68 74
69 75 class UpdateAbort(Abort):
70 76 """Raised when an update is aborted for destination issue"""
71 77
72 78 class OutOfBandError(Exception):
73 79 """Exception raised when a remote repo reports failure"""
74 80
75 81 def __init__(self, *args, **kw):
76 82 Exception.__init__(self, *args)
77 83 self.hint = kw.get('hint')
78 84
79 85 class ParseError(Exception):
80 86 """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
81 87
82 88 class UnknownIdentifier(ParseError):
83 89 """Exception raised when a {rev,file}set references an unknown identifier"""
84 90
85 91 def __init__(self, function, symbols):
86 92 from .i18n import _
87 93 ParseError.__init__(self, _("unknown identifier: %s") % function)
88 94 self.function = function
89 95 self.symbols = symbols
90 96
91 97 class RepoError(HintException):
92 98 pass
93 99
94 100 class RepoLookupError(RepoError):
95 101 pass
96 102
97 103 class FilteredRepoLookupError(RepoLookupError):
98 104 pass
99 105
100 106 class CapabilityError(RepoError):
101 107 pass
102 108
103 109 class RequirementError(RepoError):
104 110 """Exception raised if .hg/requires has an unknown entry."""
105 111 pass
106 112
107 113 class LockError(IOError):
108 114 def __init__(self, errno, strerror, filename, desc):
109 115 IOError.__init__(self, errno, strerror, filename)
110 116 self.desc = desc
111 117
112 118 class LockHeld(LockError):
113 119 def __init__(self, errno, filename, desc, locker):
114 120 LockError.__init__(self, errno, 'Lock held', filename, desc)
115 121 self.locker = locker
116 122
117 123 class LockUnavailable(LockError):
118 124 pass
119 125
120 126 # LockError is for errors while acquiring the lock -- this is unrelated
121 127 class LockInheritanceContractViolation(RuntimeError):
122 128 pass
123 129
124 130 class ResponseError(Exception):
125 131 """Raised to print an error with part of output and exit."""
126 132
127 133 class UnknownCommand(Exception):
128 134 """Exception raised if command is not in the command table."""
129 135
130 136 class AmbiguousCommand(Exception):
131 137 """Exception raised if command shortcut matches more than one command."""
132 138
133 139 # derived from KeyboardInterrupt to simplify some breakout code
134 140 class SignalInterrupt(KeyboardInterrupt):
135 141 """Exception raised on SIGTERM and SIGHUP."""
136 142
137 143 class SignatureError(Exception):
138 144 pass
139 145
140 146 class PushRaced(RuntimeError):
141 147 """An exception raised during unbundling that indicate a push race"""
142 148
143 149 # bundle2 related errors
144 150 class BundleValueError(ValueError):
145 151 """error raised when bundle2 cannot be processed"""
146 152
147 153 class BundleUnknownFeatureError(BundleValueError):
148 154 def __init__(self, parttype=None, params=(), values=()):
149 155 self.parttype = parttype
150 156 self.params = params
151 157 self.values = values
152 158 if self.parttype is None:
153 159 msg = 'Stream Parameter'
154 160 else:
155 161 msg = parttype
156 162 entries = self.params
157 163 if self.params and self.values:
158 164 assert len(self.params) == len(self.values)
159 165 entries = []
160 166 for idx, par in enumerate(self.params):
161 167 val = self.values[idx]
162 168 if val is None:
163 169 entries.append(val)
164 170 else:
165 171 entries.append("%s=%r" % (par, val))
166 172 if entries:
167 173 msg = '%s - %s' % (msg, ', '.join(entries))
168 174 ValueError.__init__(self, msg)
169 175
170 176 class ReadOnlyPartError(RuntimeError):
171 177 """error raised when code tries to alter a part being generated"""
172 178 pass
173 179
174 180 class PushkeyFailed(Abort):
175 181 """error raised when a pushkey part failed to update a value"""
176 182
177 183 def __init__(self, partid, namespace=None, key=None, new=None, old=None,
178 184 ret=None):
179 185 self.partid = partid
180 186 self.namespace = namespace
181 187 self.key = key
182 188 self.new = new
183 189 self.old = old
184 190 self.ret = ret
185 191 # no i18n expected to be processed into a better message
186 192 Abort.__init__(self, 'failed to update value for "%s/%s"'
187 193 % (namespace, key))
188 194
189 195 class CensoredNodeError(RevlogError):
190 196 """error raised when content verification fails on a censored node
191 197
192 198 Also contains the tombstone data substituted for the uncensored data.
193 199 """
194 200
195 201 def __init__(self, filename, node, tombstone):
196 202 from .node import short
197 203 RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
198 204 self.tombstone = tombstone
199 205
200 206 class CensoredBaseError(RevlogError):
201 207 """error raised when a delta is rejected because its base is censored
202 208
203 209 A delta based on a censored revision must be formed as single patch
204 210 operation which replaces the entire base with new content. This ensures
205 211 the delta may be applied by clones which have not censored the base.
206 212 """
207 213
208 214 class InvalidBundleSpecification(Exception):
209 215 """error raised when a bundle specification is invalid.
210 216
211 217 This is used for syntax errors as opposed to support errors.
212 218 """
213 219
214 220 class UnsupportedBundleSpecification(Exception):
215 221 """error raised when a bundle specification is not supported."""
@@ -1,214 +1,215 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 __future__ import absolute_import
9 9
10 10 import os
11 11 import sys
12 12 import time
13 13
14 14 from .i18n import _
15 15 from . import (
16 16 demandimport,
17 17 error,
18 18 extensions,
19 19 util,
20 20 )
21 21
22 22 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
23 23 '''call python hook. hook is callable object, looked up as
24 24 name in python module. if callable returns "true", hook
25 25 fails, else passes. if hook raises exception, treated as
26 26 hook failure. exception propagates if throw is "true".
27 27
28 28 reason for "true" meaning "hook failed" is so that
29 29 unmodified commands (e.g. mercurial.commands.update) can
30 30 be run as hooks without wrappers to convert return values.'''
31 31
32 32 if callable(funcname):
33 33 obj = funcname
34 34 funcname = obj.__module__ + "." + obj.__name__
35 35 else:
36 36 d = funcname.rfind('.')
37 37 if d == -1:
38 raise error.Abort(_('%s hook is invalid ("%s" not in '
39 'a module)') % (hname, funcname))
38 raise error.HookLoadError(
39 _('%s hook is invalid ("%s" not in a module)')
40 % (hname, funcname))
40 41 modname = funcname[:d]
41 42 oldpaths = sys.path
42 43 if util.mainfrozen():
43 44 # binary installs require sys.path manipulation
44 45 modpath, modfile = os.path.split(modname)
45 46 if modpath and modfile:
46 47 sys.path = sys.path[:] + [modpath]
47 48 modname = modfile
48 49 with demandimport.deactivated():
49 50 try:
50 51 obj = __import__(modname)
51 52 except ImportError:
52 53 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
53 54 try:
54 55 # extensions are loaded with hgext_ prefix
55 56 obj = __import__("hgext_%s" % modname)
56 57 except ImportError:
57 58 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
58 59 if ui.tracebackflag:
59 60 ui.warn(_('exception from first failed import '
60 61 'attempt:\n'))
61 62 ui.traceback(e1)
62 63 if ui.tracebackflag:
63 64 ui.warn(_('exception from second failed import '
64 65 'attempt:\n'))
65 66 ui.traceback(e2)
66 raise error.Abort(_('%s hook is invalid '
67 '(import of "%s" failed)') %
68 (hname, modname))
67 raise error.HookLoadError(
68 _('%s hook is invalid (import of "%s" failed)') %
69 (hname, modname))
69 70 sys.path = oldpaths
70 71 try:
71 72 for p in funcname.split('.')[1:]:
72 73 obj = getattr(obj, p)
73 74 except AttributeError:
74 raise error.Abort(_('%s hook is invalid '
75 '("%s" is not defined)') %
76 (hname, funcname))
75 raise error.HookLoadError(
76 _('%s hook is invalid ("%s" is not defined)')
77 % (hname, funcname))
77 78 if not callable(obj):
78 raise error.Abort(_('%s hook is invalid '
79 '("%s" is not callable)') %
80 (hname, funcname))
79 raise error.HookLoadError(
80 _('%s hook is invalid ("%s" is not callable)')
81 % (hname, funcname))
81 82
82 83 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
83 84 starttime = time.time()
84 85
85 86 try:
86 87 # redirect IO descriptors to the ui descriptors so hooks
87 88 # that write directly to these don't mess up the command
88 89 # protocol when running through the command server
89 90 old = sys.stdout, sys.stderr, sys.stdin
90 91 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
91 92
92 93 r = obj(ui=ui, repo=repo, hooktype=name, **args)
93 94 except Exception as exc:
94 95 if isinstance(exc, error.Abort):
95 96 ui.warn(_('error: %s hook failed: %s\n') %
96 97 (hname, exc.args[0]))
97 98 else:
98 99 ui.warn(_('error: %s hook raised an exception: '
99 100 '%s\n') % (hname, exc))
100 101 if throw:
101 102 raise
102 103 ui.traceback()
103 104 return True
104 105 finally:
105 106 sys.stdout, sys.stderr, sys.stdin = old
106 107 duration = time.time() - starttime
107 108 ui.log('pythonhook', 'pythonhook-%s: %s finished in %0.2f seconds\n',
108 109 name, funcname, duration)
109 110 if r:
110 111 if throw:
111 112 raise error.HookAbort(_('%s hook failed') % hname)
112 113 ui.warn(_('warning: %s hook failed\n') % hname)
113 114 return r
114 115
115 116 def _exthook(ui, repo, name, cmd, args, throw):
116 117 ui.note(_("running hook %s: %s\n") % (name, cmd))
117 118
118 119 starttime = time.time()
119 120 env = {}
120 121 for k, v in args.iteritems():
121 122 if callable(v):
122 123 v = v()
123 124 if isinstance(v, dict):
124 125 # make the dictionary element order stable across Python
125 126 # implementations
126 127 v = ('{' +
127 128 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
128 129 '}')
129 130 env['HG_' + k.upper()] = v
130 131
131 132 if repo:
132 133 cwd = repo.root
133 134 else:
134 135 cwd = os.getcwd()
135 136 r = ui.system(cmd, environ=env, cwd=cwd)
136 137
137 138 duration = time.time() - starttime
138 139 ui.log('exthook', 'exthook-%s: %s finished in %0.2f seconds\n',
139 140 name, cmd, duration)
140 141 if r:
141 142 desc, r = util.explainexit(r)
142 143 if throw:
143 144 raise error.HookAbort(_('%s hook %s') % (name, desc))
144 145 ui.warn(_('warning: %s hook %s\n') % (name, desc))
145 146 return r
146 147
147 148 def _allhooks(ui):
148 149 hooks = []
149 150 for name, cmd in ui.configitems('hooks'):
150 151 if not name.startswith('priority'):
151 152 priority = ui.configint('hooks', 'priority.%s' % name, 0)
152 153 hooks.append((-priority, len(hooks), name, cmd))
153 154 return [(k, v) for p, o, k, v in sorted(hooks)]
154 155
155 156 _redirect = False
156 157 def redirect(state):
157 158 global _redirect
158 159 _redirect = state
159 160
160 161 def hook(ui, repo, name, throw=False, **args):
161 162 if not ui.callhooks:
162 163 return False
163 164
164 165 r = False
165 166 oldstdout = -1
166 167
167 168 try:
168 169 for hname, cmd in _allhooks(ui):
169 170 if hname.split('.')[0] != name or not cmd:
170 171 continue
171 172
172 173 if oldstdout == -1 and _redirect:
173 174 try:
174 175 stdoutno = sys.__stdout__.fileno()
175 176 stderrno = sys.__stderr__.fileno()
176 177 # temporarily redirect stdout to stderr, if possible
177 178 if stdoutno >= 0 and stderrno >= 0:
178 179 sys.__stdout__.flush()
179 180 oldstdout = os.dup(stdoutno)
180 181 os.dup2(stderrno, stdoutno)
181 182 except (OSError, AttributeError):
182 183 # files seem to be bogus, give up on redirecting (WSGI, etc)
183 184 pass
184 185
185 186 if callable(cmd):
186 187 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
187 188 elif cmd.startswith('python:'):
188 189 if cmd.count(':') >= 2:
189 190 path, cmd = cmd[7:].rsplit(':', 1)
190 191 path = util.expandpath(path)
191 192 if repo:
192 193 path = os.path.join(repo.root, path)
193 194 try:
194 195 mod = extensions.loadpath(path, 'hghook.%s' % hname)
195 196 except Exception:
196 197 ui.write(_("loading %s hook failed:\n") % hname)
197 198 raise
198 199 hookfn = getattr(mod, cmd)
199 200 else:
200 201 hookfn = cmd[7:].strip()
201 202 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
202 203 else:
203 204 r = _exthook(ui, repo, hname, cmd, args, throw) or r
204 205
205 206 # The stderr is fully buffered on Windows when connected to a pipe.
206 207 # A forcible flush is required to make small stderr data in the
207 208 # remote side available to the client immediately.
208 209 sys.stderr.flush()
209 210 finally:
210 211 if _redirect and oldstdout >= 0:
211 212 os.dup2(oldstdout, stdoutno)
212 213 os.close(oldstdout)
213 214
214 215 return r
@@ -1,710 +1,710 b''
1 1 commit hooks can see env vars
2 2 (and post-transaction one are run unlocked)
3 3
4 4 $ cat << EOF >> $HGRCPATH
5 5 > [experimental]
6 6 > # drop me once bundle2 is the default,
7 7 > # added to get test change early.
8 8 > bundle2-exp = True
9 9 > EOF
10 10
11 11 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
12 12 > def showargs(ui, repo, hooktype, **kwargs):
13 13 > ui.write('%s python hook: %s\n' % (hooktype, ','.join(sorted(kwargs))))
14 14 > EOF
15 15
16 16 $ hg init a
17 17 $ cd a
18 18 $ cat > .hg/hgrc <<EOF
19 19 > [hooks]
20 20 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py commit"
21 21 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py commit.b"
22 22 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py precommit"
23 23 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxncommit"
24 24 > pretxncommit.tip = hg -q tip
25 25 > pre-identify = printenv.py pre-identify 1
26 26 > pre-cat = printenv.py pre-cat
27 27 > post-cat = printenv.py post-cat
28 28 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnopen"
29 29 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnclose"
30 30 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py txnclose"
31 31 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
32 32 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py txnabort"
33 33 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
34 34 > EOF
35 35 $ echo a > a
36 36 $ hg add a
37 37 $ hg commit -m a
38 38 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
39 39 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
40 40 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
41 41 0:cb9a9f314b8b
42 42 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
43 43 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
44 44 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
45 45 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
46 46
47 47 $ hg clone . ../b
48 48 updating to branch default
49 49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 50 $ cd ../b
51 51
52 52 changegroup hooks can see env vars
53 53
54 54 $ cat > .hg/hgrc <<EOF
55 55 > [hooks]
56 56 > prechangegroup = printenv.py prechangegroup
57 57 > changegroup = printenv.py changegroup
58 58 > incoming = printenv.py incoming
59 59 > EOF
60 60
61 61 pretxncommit and commit hooks can see both parents of merge
62 62
63 63 $ cd ../a
64 64 $ echo b >> a
65 65 $ hg commit -m a1 -d "1 0"
66 66 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
67 67 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
68 68 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
69 69 1:ab228980c14d
70 70 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
71 71 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
72 72 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
73 73 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
74 74 $ hg update -C 0
75 75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 76 $ echo b > b
77 77 $ hg add b
78 78 $ hg commit -m b -d '1 0'
79 79 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
80 80 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
81 81 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
82 82 2:ee9deb46ab31
83 83 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
84 84 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
85 85 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
86 86 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
87 87 created new head
88 88 $ hg merge 1
89 89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 (branch merge, don't forget to commit)
91 91 $ hg commit -m merge -d '2 0'
92 92 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
93 93 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
94 94 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
95 95 3:07f3376c1e65
96 96 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
97 97 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
98 98 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
99 99 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
100 100
101 101 test generic hooks
102 102
103 103 $ hg id
104 104 pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[]
105 105 abort: pre-identify hook exited with status 1
106 106 [255]
107 107 $ hg cat b
108 108 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
109 109 b
110 110 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
111 111
112 112 $ cd ../b
113 113 $ hg pull ../a
114 114 pulling from ../a
115 115 searching for changes
116 116 prechangegroup hook: HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
117 117 adding changesets
118 118 adding manifests
119 119 adding file changes
120 120 added 3 changesets with 2 changes to 2 files
121 121 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
122 122 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
123 123 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
124 124 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
125 125 (run 'hg update' to get a working copy)
126 126
127 127 tag hooks can see env vars
128 128
129 129 $ cd ../a
130 130 $ cat >> .hg/hgrc <<EOF
131 131 > pretag = printenv.py pretag
132 132 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py tag"
133 133 > EOF
134 134 $ hg tag -d '3 0' a
135 135 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
136 136 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
137 137 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
138 138 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
139 139 4:539e4b31b6dc
140 140 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
141 141 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
142 142 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
143 143 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
144 144 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
145 145 $ hg tag -l la
146 146 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
147 147 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
148 148
149 149 pretag hook can forbid tagging
150 150
151 151 $ echo "pretag.forbid = printenv.py pretag.forbid 1" >> .hg/hgrc
152 152 $ hg tag -d '4 0' fa
153 153 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
154 154 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
155 155 abort: pretag.forbid hook exited with status 1
156 156 [255]
157 157 $ hg tag -l fla
158 158 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
159 159 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
160 160 abort: pretag.forbid hook exited with status 1
161 161 [255]
162 162
163 163 pretxncommit hook can see changeset, can roll back txn, changeset no
164 164 more there after
165 165
166 166 $ echo "pretxncommit.forbid0 = hg tip -q" >> .hg/hgrc
167 167 $ echo "pretxncommit.forbid1 = printenv.py pretxncommit.forbid 1" >> .hg/hgrc
168 168 $ echo z > z
169 169 $ hg add z
170 170 $ hg -q tip
171 171 4:539e4b31b6dc
172 172 $ hg commit -m 'fail' -d '4 0'
173 173 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
174 174 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
175 175 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
176 176 5:6f611f8018c1
177 177 5:6f611f8018c1
178 178 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
179 179 transaction abort!
180 180 txnabort python hook: txnid,txnname
181 181 txnabort hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
182 182 rollback completed
183 183 abort: pretxncommit.forbid1 hook exited with status 1
184 184 [255]
185 185 $ hg -q tip
186 186 4:539e4b31b6dc
187 187
188 188 (Check that no 'changelog.i.a' file were left behind)
189 189
190 190 $ ls -1 .hg/store/
191 191 00changelog.i
192 192 00manifest.i
193 193 data
194 194 fncache
195 195 journal.phaseroots
196 196 phaseroots
197 197 undo
198 198 undo.backup.fncache
199 199 undo.backupfiles
200 200 undo.phaseroots
201 201
202 202
203 203 precommit hook can prevent commit
204 204
205 205 $ echo "precommit.forbid = printenv.py precommit.forbid 1" >> .hg/hgrc
206 206 $ hg commit -m 'fail' -d '4 0'
207 207 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
208 208 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
209 209 abort: precommit.forbid hook exited with status 1
210 210 [255]
211 211 $ hg -q tip
212 212 4:539e4b31b6dc
213 213
214 214 preupdate hook can prevent update
215 215
216 216 $ echo "preupdate = printenv.py preupdate" >> .hg/hgrc
217 217 $ hg update 1
218 218 preupdate hook: HG_PARENT1=ab228980c14d
219 219 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
220 220
221 221 update hook
222 222
223 223 $ echo "update = printenv.py update" >> .hg/hgrc
224 224 $ hg update
225 225 preupdate hook: HG_PARENT1=539e4b31b6dc
226 226 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 227 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
228 228
229 229 pushkey hook
230 230
231 231 $ echo "pushkey = printenv.py pushkey" >> .hg/hgrc
232 232 $ cd ../b
233 233 $ hg bookmark -r null foo
234 234 $ hg push -B foo ../a
235 235 pushing to ../a
236 236 searching for changes
237 237 no changes found
238 238 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=push (glob)
239 239 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_PENDING=$TESTTMP/a HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
240 240 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
241 241 txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
242 242 exporting bookmark foo
243 243 [1]
244 244 $ cd ../a
245 245
246 246 listkeys hook
247 247
248 248 $ echo "listkeys = printenv.py listkeys" >> .hg/hgrc
249 249 $ hg bookmark -r null bar
250 250 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
251 251 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
252 252 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
253 253 $ cd ../b
254 254 $ hg pull -B bar ../a
255 255 pulling from ../a
256 256 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
257 257 no changes found
258 258 listkeys hook: HG_NAMESPACE=phase HG_VALUES={}
259 259 adding remote bookmark bar
260 260 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
261 261 $ cd ../a
262 262
263 263 test that prepushkey can prevent incoming keys
264 264
265 265 $ echo "prepushkey = printenv.py prepushkey.forbid 1" >> .hg/hgrc
266 266 $ cd ../b
267 267 $ hg bookmark -r null baz
268 268 $ hg push -B baz ../a
269 269 pushing to ../a
270 270 searching for changes
271 271 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
272 272 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
273 273 no changes found
274 274 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=push (glob)
275 275 prepushkey.forbid hook: HG_BUNDLE2=1 HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_SOURCE=push HG_TXNID=TXN:* HG_URL=push (glob)
276 276 pushkey-abort: prepushkey hook exited with status 1
277 277 abort: exporting bookmark baz failed!
278 278 [255]
279 279 $ cd ../a
280 280
281 281 test that prelistkeys can prevent listing keys
282 282
283 283 $ echo "prelistkeys = printenv.py prelistkeys.forbid 1" >> .hg/hgrc
284 284 $ hg bookmark -r null quux
285 285 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
286 286 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
287 287 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
288 288 $ cd ../b
289 289 $ hg pull -B quux ../a
290 290 pulling from ../a
291 291 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
292 292 abort: prelistkeys hook exited with status 1
293 293 [255]
294 294 $ cd ../a
295 295 $ rm .hg/hgrc
296 296
297 297 prechangegroup hook can prevent incoming changes
298 298
299 299 $ cd ../b
300 300 $ hg -q tip
301 301 3:07f3376c1e65
302 302 $ cat > .hg/hgrc <<EOF
303 303 > [hooks]
304 304 > prechangegroup.forbid = printenv.py prechangegroup.forbid 1
305 305 > EOF
306 306 $ hg pull ../a
307 307 pulling from ../a
308 308 searching for changes
309 309 prechangegroup.forbid hook: HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
310 310 abort: prechangegroup.forbid hook exited with status 1
311 311 [255]
312 312
313 313 pretxnchangegroup hook can see incoming changes, can roll back txn,
314 314 incoming changes no longer there after
315 315
316 316 $ cat > .hg/hgrc <<EOF
317 317 > [hooks]
318 318 > pretxnchangegroup.forbid0 = hg tip -q
319 319 > pretxnchangegroup.forbid1 = printenv.py pretxnchangegroup.forbid 1
320 320 > EOF
321 321 $ hg pull ../a
322 322 pulling from ../a
323 323 searching for changes
324 324 adding changesets
325 325 adding manifests
326 326 adding file changes
327 327 added 1 changesets with 1 changes to 1 files
328 328 4:539e4b31b6dc
329 329 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
330 330 transaction abort!
331 331 rollback completed
332 332 abort: pretxnchangegroup.forbid1 hook exited with status 1
333 333 [255]
334 334 $ hg -q tip
335 335 3:07f3376c1e65
336 336
337 337 outgoing hooks can see env vars
338 338
339 339 $ rm .hg/hgrc
340 340 $ cat > ../a/.hg/hgrc <<EOF
341 341 > [hooks]
342 342 > preoutgoing = printenv.py preoutgoing
343 343 > outgoing = printenv.py outgoing
344 344 > EOF
345 345 $ hg pull ../a
346 346 pulling from ../a
347 347 searching for changes
348 348 preoutgoing hook: HG_SOURCE=pull
349 349 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
350 350 adding changesets
351 351 adding manifests
352 352 adding file changes
353 353 added 1 changesets with 1 changes to 1 files
354 354 adding remote bookmark quux
355 355 (run 'hg update' to get a working copy)
356 356 $ hg rollback
357 357 repository tip rolled back to revision 3 (undo pull)
358 358
359 359 preoutgoing hook can prevent outgoing changes
360 360
361 361 $ echo "preoutgoing.forbid = printenv.py preoutgoing.forbid 1" >> ../a/.hg/hgrc
362 362 $ hg pull ../a
363 363 pulling from ../a
364 364 searching for changes
365 365 preoutgoing hook: HG_SOURCE=pull
366 366 preoutgoing.forbid hook: HG_SOURCE=pull
367 367 abort: preoutgoing.forbid hook exited with status 1
368 368 [255]
369 369
370 370 outgoing hooks work for local clones
371 371
372 372 $ cd ..
373 373 $ cat > a/.hg/hgrc <<EOF
374 374 > [hooks]
375 375 > preoutgoing = printenv.py preoutgoing
376 376 > outgoing = printenv.py outgoing
377 377 > EOF
378 378 $ hg clone a c
379 379 preoutgoing hook: HG_SOURCE=clone
380 380 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
381 381 updating to branch default
382 382 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 383 $ rm -rf c
384 384
385 385 preoutgoing hook can prevent outgoing changes for local clones
386 386
387 387 $ echo "preoutgoing.forbid = printenv.py preoutgoing.forbid 1" >> a/.hg/hgrc
388 388 $ hg clone a zzz
389 389 preoutgoing hook: HG_SOURCE=clone
390 390 preoutgoing.forbid hook: HG_SOURCE=clone
391 391 abort: preoutgoing.forbid hook exited with status 1
392 392 [255]
393 393
394 394 $ cd "$TESTTMP/b"
395 395
396 396 $ cat > hooktests.py <<EOF
397 397 > from mercurial import error
398 398 >
399 399 > uncallable = 0
400 400 >
401 401 > def printargs(args):
402 402 > args.pop('ui', None)
403 403 > args.pop('repo', None)
404 404 > a = list(args.items())
405 405 > a.sort()
406 406 > print 'hook args:'
407 407 > for k, v in a:
408 408 > print ' ', k, v
409 409 >
410 410 > def passhook(**args):
411 411 > printargs(args)
412 412 >
413 413 > def failhook(**args):
414 414 > printargs(args)
415 415 > return True
416 416 >
417 417 > class LocalException(Exception):
418 418 > pass
419 419 >
420 420 > def raisehook(**args):
421 421 > raise LocalException('exception from hook')
422 422 >
423 423 > def aborthook(**args):
424 424 > raise error.Abort('raise abort from hook')
425 425 >
426 426 > def brokenhook(**args):
427 427 > return 1 + {}
428 428 >
429 429 > def verbosehook(ui, **args):
430 430 > ui.note('verbose output from hook\n')
431 431 >
432 432 > def printtags(ui, repo, **args):
433 433 > print sorted(repo.tags())
434 434 >
435 435 > class container:
436 436 > unreachable = 1
437 437 > EOF
438 438
439 439 test python hooks
440 440
441 441 #if windows
442 442 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
443 443 #else
444 444 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
445 445 #endif
446 446 $ export PYTHONPATH
447 447
448 448 $ echo '[hooks]' > ../a/.hg/hgrc
449 449 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
450 450 $ hg pull ../a 2>&1 | grep 'raised an exception'
451 451 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
452 452
453 453 $ echo '[hooks]' > ../a/.hg/hgrc
454 454 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
455 455 $ hg pull ../a 2>&1 | grep 'raised an exception'
456 456 error: preoutgoing.raise hook raised an exception: exception from hook
457 457
458 458 $ echo '[hooks]' > ../a/.hg/hgrc
459 459 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
460 460 $ hg pull ../a
461 461 pulling from ../a
462 462 searching for changes
463 463 error: preoutgoing.abort hook failed: raise abort from hook
464 464 abort: raise abort from hook
465 465 [255]
466 466
467 467 $ echo '[hooks]' > ../a/.hg/hgrc
468 468 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
469 469 $ hg pull ../a
470 470 pulling from ../a
471 471 searching for changes
472 472 hook args:
473 473 hooktype preoutgoing
474 474 source pull
475 475 abort: preoutgoing.fail hook failed
476 476 [255]
477 477
478 478 $ echo '[hooks]' > ../a/.hg/hgrc
479 479 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
480 480 $ hg pull ../a
481 481 pulling from ../a
482 482 searching for changes
483 483 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
484 484 [255]
485 485
486 486 $ echo '[hooks]' > ../a/.hg/hgrc
487 487 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
488 488 $ hg pull ../a
489 489 pulling from ../a
490 490 searching for changes
491 491 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
492 492 [255]
493 493
494 494 $ echo '[hooks]' > ../a/.hg/hgrc
495 495 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
496 496 $ hg pull ../a
497 497 pulling from ../a
498 498 searching for changes
499 499 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
500 500 [255]
501 501
502 502 $ echo '[hooks]' > ../a/.hg/hgrc
503 503 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
504 504 $ hg pull ../a
505 505 pulling from ../a
506 506 searching for changes
507 507 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
508 508 [255]
509 509
510 510 $ echo '[hooks]' > ../a/.hg/hgrc
511 511 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
512 512 $ hg pull ../a
513 513 pulling from ../a
514 514 searching for changes
515 515 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
516 516 [255]
517 517
518 518 $ echo '[hooks]' > ../a/.hg/hgrc
519 519 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
520 520 $ hg pull ../a
521 521 pulling from ../a
522 522 searching for changes
523 523 hook args:
524 524 hooktype preoutgoing
525 525 source pull
526 526 adding changesets
527 527 adding manifests
528 528 adding file changes
529 529 added 1 changesets with 1 changes to 1 files
530 530 adding remote bookmark quux
531 531 (run 'hg update' to get a working copy)
532 532
533 533 make sure --traceback works
534 534
535 535 $ echo '[hooks]' > .hg/hgrc
536 536 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
537 537
538 538 $ echo aa > a
539 539 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
540 540 Traceback (most recent call last):
541 541
542 542 $ cd ..
543 543 $ hg init c
544 544 $ cd c
545 545
546 546 $ cat > hookext.py <<EOF
547 547 > def autohook(**args):
548 548 > print "Automatically installed hook"
549 549 >
550 550 > def reposetup(ui, repo):
551 551 > repo.ui.setconfig("hooks", "commit.auto", autohook)
552 552 > EOF
553 553 $ echo '[extensions]' >> .hg/hgrc
554 554 $ echo 'hookext = hookext.py' >> .hg/hgrc
555 555
556 556 $ touch foo
557 557 $ hg add foo
558 558 $ hg ci -d '0 0' -m 'add foo'
559 559 Automatically installed hook
560 560 $ echo >> foo
561 561 $ hg ci --debug -d '0 0' -m 'change foo'
562 562 committing files:
563 563 foo
564 564 committing manifest
565 565 committing changelog
566 566 calling hook commit.auto: hgext_hookext.autohook
567 567 Automatically installed hook
568 568 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
569 569
570 570 $ hg showconfig hooks
571 571 hooks.commit.auto=<function autohook at *> (glob)
572 572
573 573 test python hook configured with python:[file]:[hook] syntax
574 574
575 575 $ cd ..
576 576 $ mkdir d
577 577 $ cd d
578 578 $ hg init repo
579 579 $ mkdir hooks
580 580
581 581 $ cd hooks
582 582 $ cat > testhooks.py <<EOF
583 583 > def testhook(**args):
584 584 > print 'hook works'
585 585 > EOF
586 586 $ echo '[hooks]' > ../repo/.hg/hgrc
587 587 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
588 588
589 589 $ cd ../repo
590 590 $ hg commit -d '0 0'
591 591 hook works
592 592 nothing changed
593 593 [1]
594 594
595 595 $ echo '[hooks]' > .hg/hgrc
596 596 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
597 597 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
598 598
599 599 $ hg up null
600 600 loading update.ne hook failed:
601 601 abort: No such file or directory: $TESTTMP/d/repo/nonexistent.py
602 602 [255]
603 603
604 604 $ hg id
605 605 loading pre-identify.npmd hook failed:
606 606 abort: No module named repo!
607 607 [255]
608 608
609 609 $ cd ../../b
610 610
611 611 make sure --traceback works on hook import failure
612 612
613 613 $ cat > importfail.py <<EOF
614 614 > import somebogusmodule
615 615 > # dereference something in the module to force demandimport to load it
616 616 > somebogusmodule.whatever
617 617 > EOF
618 618
619 619 $ echo '[hooks]' > .hg/hgrc
620 620 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
621 621
622 622 $ echo a >> a
623 623 $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])'
624 624 exception from first failed import attempt:
625 625 Traceback (most recent call last):
626 626 ImportError: No module named somebogusmodule
627 627 exception from second failed import attempt:
628 628 Traceback (most recent call last):
629 629 ImportError: No module named hgext_importfail
630 630 Traceback (most recent call last):
631 Abort: precommit.importfail hook is invalid (import of "importfail" failed)
631 HookLoadError: precommit.importfail hook is invalid (import of "importfail" failed)
632 632 abort: precommit.importfail hook is invalid (import of "importfail" failed)
633 633
634 634 Issue1827: Hooks Update & Commit not completely post operation
635 635
636 636 commit and update hooks should run after command completion. The largefiles
637 637 use demonstrates a recursive wlock, showing the hook doesn't run until the
638 638 final release (and dirstate flush).
639 639
640 640 $ echo '[hooks]' > .hg/hgrc
641 641 $ echo 'commit = hg id' >> .hg/hgrc
642 642 $ echo 'update = hg id' >> .hg/hgrc
643 643 $ echo bb > a
644 644 $ hg ci -ma
645 645 223eafe2750c tip
646 646 $ hg up 0 --config extensions.largefiles=
647 647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 648 cb9a9f314b8b
649 649
650 650 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
651 651 that is passed to pre/post hooks
652 652
653 653 $ echo '[hooks]' > .hg/hgrc
654 654 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
655 655 $ hg id
656 656 cb9a9f314b8b
657 657 $ hg id --verbose
658 658 calling hook pre-identify: hooktests.verbosehook
659 659 verbose output from hook
660 660 cb9a9f314b8b
661 661
662 662 Ensure hooks can be prioritized
663 663
664 664 $ echo '[hooks]' > .hg/hgrc
665 665 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
666 666 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
667 667 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
668 668 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
669 669 $ hg id --verbose
670 670 calling hook pre-identify.b: hooktests.verbosehook
671 671 verbose output from hook
672 672 calling hook pre-identify.a: hooktests.verbosehook
673 673 verbose output from hook
674 674 calling hook pre-identify.c: hooktests.verbosehook
675 675 verbose output from hook
676 676 cb9a9f314b8b
677 677
678 678 new tags must be visible in pretxncommit (issue3210)
679 679
680 680 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
681 681 $ hg tag -f foo
682 682 ['a', 'foo', 'tip']
683 683
684 684 new commits must be visible in pretxnchangegroup (issue3428)
685 685
686 686 $ cd ..
687 687 $ hg init to
688 688 $ echo '[hooks]' >> to/.hg/hgrc
689 689 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
690 690 $ echo a >> to/a
691 691 $ hg --cwd to ci -Ama
692 692 adding a
693 693 $ hg clone to from
694 694 updating to branch default
695 695 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
696 696 $ echo aa >> from/a
697 697 $ hg --cwd from ci -mb
698 698 $ hg --cwd from push
699 699 pushing to $TESTTMP/to (glob)
700 700 searching for changes
701 701 adding changesets
702 702 adding manifests
703 703 adding file changes
704 704 added 1 changesets with 1 changes to 1 files
705 705 changeset: 1:9836a07b9b9d
706 706 tag: tip
707 707 user: test
708 708 date: Thu Jan 01 00:00:00 1970 +0000
709 709 summary: b
710 710
General Comments 0
You need to be logged in to leave comments. Login now