##// END OF EJS Templates
dispatch: print 'abort:' when a pre-command hook fails (BC)...
Siddharth Agarwal -
r19011:12acbea1 default
parent child Browse files
Show More
@@ -1,844 +1,842 b''
1 1 # dispatch.py - command dispatching for mercurial
2 2 #
3 3 # Copyright 2005-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, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as uimod
13 13
14 14 class request(object):
15 15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 16 ferr=None):
17 17 self.args = args
18 18 self.ui = ui
19 19 self.repo = repo
20 20
21 21 # input/output/error streams
22 22 self.fin = fin
23 23 self.fout = fout
24 24 self.ferr = ferr
25 25
26 26 def run():
27 27 "run the command in sys.argv"
28 28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29 29
30 30 def dispatch(req):
31 31 "run the command specified in req.args"
32 32 if req.ferr:
33 33 ferr = req.ferr
34 34 elif req.ui:
35 35 ferr = req.ui.ferr
36 36 else:
37 37 ferr = sys.stderr
38 38
39 39 try:
40 40 if not req.ui:
41 41 req.ui = uimod.ui()
42 42 if '--traceback' in req.args:
43 43 req.ui.setconfig('ui', 'traceback', 'on')
44 44
45 45 # set ui streams from the request
46 46 if req.fin:
47 47 req.ui.fin = req.fin
48 48 if req.fout:
49 49 req.ui.fout = req.fout
50 50 if req.ferr:
51 51 req.ui.ferr = req.ferr
52 52 except util.Abort, inst:
53 53 ferr.write(_("abort: %s\n") % inst)
54 54 if inst.hint:
55 55 ferr.write(_("(%s)\n") % inst.hint)
56 56 return -1
57 57 except error.ParseError, inst:
58 58 if len(inst.args) > 1:
59 59 ferr.write(_("hg: parse error at %s: %s\n") %
60 60 (inst.args[1], inst.args[0]))
61 61 else:
62 62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 63 return -1
64 64
65 65 return _runcatch(req)
66 66
67 67 def _runcatch(req):
68 68 def catchterm(*args):
69 69 raise error.SignalInterrupt
70 70
71 71 ui = req.ui
72 72 try:
73 73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
74 74 num = getattr(signal, name, None)
75 75 if num:
76 76 signal.signal(num, catchterm)
77 77 except ValueError:
78 78 pass # happens if called in a thread
79 79
80 80 try:
81 81 try:
82 82 # enter the debugger before command execution
83 83 if '--debugger' in req.args:
84 84 ui.warn(_("entering debugger - "
85 85 "type c to continue starting hg or h for help\n"))
86 86 pdb.set_trace()
87 87 try:
88 88 return _dispatch(req)
89 89 finally:
90 90 ui.flush()
91 91 except: # re-raises
92 92 # enter the debugger when we hit an exception
93 93 if '--debugger' in req.args:
94 94 traceback.print_exc()
95 95 pdb.post_mortem(sys.exc_info()[2])
96 96 ui.traceback()
97 97 raise
98 98
99 99 # Global exception handling, alphabetically
100 100 # Mercurial-specific first, followed by built-in and library exceptions
101 101 except error.AmbiguousCommand, inst:
102 102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
103 103 (inst.args[0], " ".join(inst.args[1])))
104 104 except error.ParseError, inst:
105 105 if len(inst.args) > 1:
106 106 ui.warn(_("hg: parse error at %s: %s\n") %
107 107 (inst.args[1], inst.args[0]))
108 108 else:
109 109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
110 110 return -1
111 111 except error.LockHeld, inst:
112 112 if inst.errno == errno.ETIMEDOUT:
113 113 reason = _('timed out waiting for lock held by %s') % inst.locker
114 114 else:
115 115 reason = _('lock held by %s') % inst.locker
116 116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
117 117 except error.LockUnavailable, inst:
118 118 ui.warn(_("abort: could not lock %s: %s\n") %
119 119 (inst.desc or inst.filename, inst.strerror))
120 120 except error.CommandError, inst:
121 121 if inst.args[0]:
122 122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
123 123 commands.help_(ui, inst.args[0], full=False, command=True)
124 124 else:
125 125 ui.warn(_("hg: %s\n") % inst.args[1])
126 126 commands.help_(ui, 'shortlist')
127 127 except error.OutOfBandError, inst:
128 128 ui.warn(_("abort: remote error:\n"))
129 129 ui.warn(''.join(inst.args))
130 130 except error.RepoError, inst:
131 131 ui.warn(_("abort: %s!\n") % inst)
132 132 if inst.hint:
133 133 ui.warn(_("(%s)\n") % inst.hint)
134 134 except error.ResponseError, inst:
135 135 ui.warn(_("abort: %s") % inst.args[0])
136 136 if not isinstance(inst.args[1], basestring):
137 137 ui.warn(" %r\n" % (inst.args[1],))
138 138 elif not inst.args[1]:
139 139 ui.warn(_(" empty string\n"))
140 140 else:
141 141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
142 142 except error.RevlogError, inst:
143 143 ui.warn(_("abort: %s!\n") % inst)
144 144 except error.SignalInterrupt:
145 145 ui.warn(_("killed!\n"))
146 146 except error.UnknownCommand, inst:
147 147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
148 148 try:
149 149 # check if the command is in a disabled extension
150 150 # (but don't check for extensions themselves)
151 151 commands.help_(ui, inst.args[0], unknowncmd=True)
152 152 except error.UnknownCommand:
153 153 commands.help_(ui, 'shortlist')
154 154 except error.InterventionRequired, inst:
155 155 ui.warn("%s\n" % inst)
156 156 return 1
157 157 except util.Abort, inst:
158 158 ui.warn(_("abort: %s\n") % inst)
159 159 if inst.hint:
160 160 ui.warn(_("(%s)\n") % inst.hint)
161 161 except ImportError, inst:
162 162 ui.warn(_("abort: %s!\n") % inst)
163 163 m = str(inst).split()[-1]
164 164 if m in "mpatch bdiff".split():
165 165 ui.warn(_("(did you forget to compile extensions?)\n"))
166 166 elif m in "zlib".split():
167 167 ui.warn(_("(is your Python install correct?)\n"))
168 168 except IOError, inst:
169 169 if util.safehasattr(inst, "code"):
170 170 ui.warn(_("abort: %s\n") % inst)
171 171 elif util.safehasattr(inst, "reason"):
172 172 try: # usually it is in the form (errno, strerror)
173 173 reason = inst.reason.args[1]
174 174 except (AttributeError, IndexError):
175 175 # it might be anything, for example a string
176 176 reason = inst.reason
177 177 ui.warn(_("abort: error: %s\n") % reason)
178 178 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
179 179 if ui.debugflag:
180 180 ui.warn(_("broken pipe\n"))
181 181 elif getattr(inst, "strerror", None):
182 182 if getattr(inst, "filename", None):
183 183 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
184 184 else:
185 185 ui.warn(_("abort: %s\n") % inst.strerror)
186 186 else:
187 187 raise
188 188 except OSError, inst:
189 189 if getattr(inst, "filename", None) is not None:
190 190 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
191 191 else:
192 192 ui.warn(_("abort: %s\n") % inst.strerror)
193 193 except KeyboardInterrupt:
194 194 try:
195 195 ui.warn(_("interrupted!\n"))
196 196 except IOError, inst:
197 197 if inst.errno == errno.EPIPE:
198 198 if ui.debugflag:
199 199 ui.warn(_("\nbroken pipe\n"))
200 200 else:
201 201 raise
202 202 except MemoryError:
203 203 ui.warn(_("abort: out of memory\n"))
204 204 except SystemExit, inst:
205 205 # Commands shouldn't sys.exit directly, but give a return code.
206 206 # Just in case catch this and and pass exit code to caller.
207 207 return inst.code
208 208 except socket.error, inst:
209 209 ui.warn(_("abort: %s\n") % inst.args[-1])
210 210 except: # re-raises
211 211 myver = util.version()
212 212 # For compatibility checking, we discard the portion of the hg
213 213 # version after the + on the assumption that if a "normal
214 214 # user" is running a build with a + in it the packager
215 215 # probably built from fairly close to a tag and anyone with a
216 216 # 'make local' copy of hg (where the version number can be out
217 217 # of date) will be clueful enough to notice the implausible
218 218 # version number and try updating.
219 219 compare = myver.split('+')[0]
220 220 ct = tuplever(compare)
221 221 worst = None, ct, ''
222 222 for name, mod in extensions.extensions():
223 223 testedwith = getattr(mod, 'testedwith', '')
224 224 report = getattr(mod, 'buglink', _('the extension author.'))
225 225 if not testedwith.strip():
226 226 # We found an untested extension. It's likely the culprit.
227 227 worst = name, 'unknown', report
228 228 break
229 229 if compare not in testedwith.split() and testedwith != 'internal':
230 230 tested = [tuplever(v) for v in testedwith.split()]
231 231 lower = [t for t in tested if t < ct]
232 232 nearest = max(lower or tested)
233 233 if worst[0] is None or nearest < worst[1]:
234 234 worst = name, nearest, report
235 235 if worst[0] is not None:
236 236 name, testedwith, report = worst
237 237 if not isinstance(testedwith, str):
238 238 testedwith = '.'.join([str(c) for c in testedwith])
239 239 warning = (_('** Unknown exception encountered with '
240 240 'possibly-broken third-party extension %s\n'
241 241 '** which supports versions %s of Mercurial.\n'
242 242 '** Please disable %s and try your action again.\n'
243 243 '** If that fixes the bug please report it to %s\n')
244 244 % (name, testedwith, name, report))
245 245 else:
246 246 warning = (_("** unknown exception encountered, "
247 247 "please report by visiting\n") +
248 248 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
249 249 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
250 250 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
251 251 (_("** Extensions loaded: %s\n") %
252 252 ", ".join([x[0] for x in extensions.extensions()])))
253 253 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
254 254 ui.warn(warning)
255 255 raise
256 256
257 257 return -1
258 258
259 259 def tuplever(v):
260 260 try:
261 261 return tuple([int(i) for i in v.split('.')])
262 262 except ValueError:
263 263 return tuple()
264 264
265 265 def aliasargs(fn, givenargs):
266 266 args = getattr(fn, 'args', [])
267 267 if args:
268 268 cmd = ' '.join(map(util.shellquote, args))
269 269
270 270 nums = []
271 271 def replacer(m):
272 272 num = int(m.group(1)) - 1
273 273 nums.append(num)
274 274 if num < len(givenargs):
275 275 return givenargs[num]
276 276 raise util.Abort(_('too few arguments for command alias'))
277 277 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
278 278 givenargs = [x for i, x in enumerate(givenargs)
279 279 if i not in nums]
280 280 args = shlex.split(cmd)
281 281 return args + givenargs
282 282
283 283 class cmdalias(object):
284 284 def __init__(self, name, definition, cmdtable):
285 285 self.name = self.cmd = name
286 286 self.cmdname = ''
287 287 self.definition = definition
288 288 self.args = []
289 289 self.opts = []
290 290 self.help = ''
291 291 self.norepo = True
292 292 self.optionalrepo = False
293 293 self.badalias = False
294 294
295 295 try:
296 296 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
297 297 for alias, e in cmdtable.iteritems():
298 298 if e is entry:
299 299 self.cmd = alias
300 300 break
301 301 self.shadows = True
302 302 except error.UnknownCommand:
303 303 self.shadows = False
304 304
305 305 if not self.definition:
306 306 def fn(ui, *args):
307 307 ui.warn(_("no definition for alias '%s'\n") % self.name)
308 308 return 1
309 309 self.fn = fn
310 310 self.badalias = True
311 311 return
312 312
313 313 if self.definition.startswith('!'):
314 314 self.shell = True
315 315 def fn(ui, *args):
316 316 env = {'HG_ARGS': ' '.join((self.name,) + args)}
317 317 def _checkvar(m):
318 318 if m.groups()[0] == '$':
319 319 return m.group()
320 320 elif int(m.groups()[0]) <= len(args):
321 321 return m.group()
322 322 else:
323 323 ui.debug("No argument found for substitution "
324 324 "of %i variable in alias '%s' definition."
325 325 % (int(m.groups()[0]), self.name))
326 326 return ''
327 327 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
328 328 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
329 329 replace['0'] = self.name
330 330 replace['@'] = ' '.join(args)
331 331 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
332 332 return util.system(cmd, environ=env, out=ui.fout)
333 333 self.fn = fn
334 334 return
335 335
336 336 args = shlex.split(self.definition)
337 337 self.cmdname = cmd = args.pop(0)
338 338 args = map(util.expandpath, args)
339 339
340 340 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
341 341 if _earlygetopt([invalidarg], args):
342 342 def fn(ui, *args):
343 343 ui.warn(_("error in definition for alias '%s': %s may only "
344 344 "be given on the command line\n")
345 345 % (self.name, invalidarg))
346 346 return 1
347 347
348 348 self.fn = fn
349 349 self.badalias = True
350 350 return
351 351
352 352 try:
353 353 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
354 354 if len(tableentry) > 2:
355 355 self.fn, self.opts, self.help = tableentry
356 356 else:
357 357 self.fn, self.opts = tableentry
358 358
359 359 self.args = aliasargs(self.fn, args)
360 360 if cmd not in commands.norepo.split(' '):
361 361 self.norepo = False
362 362 if cmd in commands.optionalrepo.split(' '):
363 363 self.optionalrepo = True
364 364 if self.help.startswith("hg " + cmd):
365 365 # drop prefix in old-style help lines so hg shows the alias
366 366 self.help = self.help[4 + len(cmd):]
367 367 self.__doc__ = self.fn.__doc__
368 368
369 369 except error.UnknownCommand:
370 370 def fn(ui, *args):
371 371 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
372 372 % (self.name, cmd))
373 373 try:
374 374 # check if the command is in a disabled extension
375 375 commands.help_(ui, cmd, unknowncmd=True)
376 376 except error.UnknownCommand:
377 377 pass
378 378 return 1
379 379 self.fn = fn
380 380 self.badalias = True
381 381 except error.AmbiguousCommand:
382 382 def fn(ui, *args):
383 383 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
384 384 % (self.name, cmd))
385 385 return 1
386 386 self.fn = fn
387 387 self.badalias = True
388 388
389 389 def __call__(self, ui, *args, **opts):
390 390 if self.shadows:
391 391 ui.debug("alias '%s' shadows command '%s'\n" %
392 392 (self.name, self.cmdname))
393 393
394 394 if util.safehasattr(self, 'shell'):
395 395 return self.fn(ui, *args, **opts)
396 396 else:
397 397 try:
398 398 util.checksignature(self.fn)(ui, *args, **opts)
399 399 except error.SignatureError:
400 400 args = ' '.join([self.cmdname] + self.args)
401 401 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
402 402 raise
403 403
404 404 def addaliases(ui, cmdtable):
405 405 # aliases are processed after extensions have been loaded, so they
406 406 # may use extension commands. Aliases can also use other alias definitions,
407 407 # but only if they have been defined prior to the current definition.
408 408 for alias, definition in ui.configitems('alias'):
409 409 aliasdef = cmdalias(alias, definition, cmdtable)
410 410
411 411 try:
412 412 olddef = cmdtable[aliasdef.cmd][0]
413 413 if olddef.definition == aliasdef.definition:
414 414 continue
415 415 except (KeyError, AttributeError):
416 416 # definition might not exist or it might not be a cmdalias
417 417 pass
418 418
419 419 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
420 420 if aliasdef.norepo:
421 421 commands.norepo += ' %s' % alias
422 422 if aliasdef.optionalrepo:
423 423 commands.optionalrepo += ' %s' % alias
424 424
425 425 def _parse(ui, args):
426 426 options = {}
427 427 cmdoptions = {}
428 428
429 429 try:
430 430 args = fancyopts.fancyopts(args, commands.globalopts, options)
431 431 except fancyopts.getopt.GetoptError, inst:
432 432 raise error.CommandError(None, inst)
433 433
434 434 if args:
435 435 cmd, args = args[0], args[1:]
436 436 aliases, entry = cmdutil.findcmd(cmd, commands.table,
437 437 ui.configbool("ui", "strict"))
438 438 cmd = aliases[0]
439 439 args = aliasargs(entry[0], args)
440 440 defaults = ui.config("defaults", cmd)
441 441 if defaults:
442 442 args = map(util.expandpath, shlex.split(defaults)) + args
443 443 c = list(entry[1])
444 444 else:
445 445 cmd = None
446 446 c = []
447 447
448 448 # combine global options into local
449 449 for o in commands.globalopts:
450 450 c.append((o[0], o[1], options[o[1]], o[3]))
451 451
452 452 try:
453 453 args = fancyopts.fancyopts(args, c, cmdoptions, True)
454 454 except fancyopts.getopt.GetoptError, inst:
455 455 raise error.CommandError(cmd, inst)
456 456
457 457 # separate global options back out
458 458 for o in commands.globalopts:
459 459 n = o[1]
460 460 options[n] = cmdoptions[n]
461 461 del cmdoptions[n]
462 462
463 463 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
464 464
465 465 def _parseconfig(ui, config):
466 466 """parse the --config options from the command line"""
467 467 configs = []
468 468
469 469 for cfg in config:
470 470 try:
471 471 name, value = cfg.split('=', 1)
472 472 section, name = name.split('.', 1)
473 473 if not section or not name:
474 474 raise IndexError
475 475 ui.setconfig(section, name, value)
476 476 configs.append((section, name, value))
477 477 except (IndexError, ValueError):
478 478 raise util.Abort(_('malformed --config option: %r '
479 479 '(use --config section.name=value)') % cfg)
480 480
481 481 return configs
482 482
483 483 def _earlygetopt(aliases, args):
484 484 """Return list of values for an option (or aliases).
485 485
486 486 The values are listed in the order they appear in args.
487 487 The options and values are removed from args.
488 488 """
489 489 try:
490 490 argcount = args.index("--")
491 491 except ValueError:
492 492 argcount = len(args)
493 493 shortopts = [opt for opt in aliases if len(opt) == 2]
494 494 values = []
495 495 pos = 0
496 496 while pos < argcount:
497 497 if args[pos] in aliases:
498 498 if pos + 1 >= argcount:
499 499 # ignore and let getopt report an error if there is no value
500 500 break
501 501 del args[pos]
502 502 values.append(args.pop(pos))
503 503 argcount -= 2
504 504 elif args[pos][:2] in shortopts:
505 505 # short option can have no following space, e.g. hg log -Rfoo
506 506 values.append(args.pop(pos)[2:])
507 507 argcount -= 1
508 508 else:
509 509 pos += 1
510 510 return values
511 511
512 512 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
513 513 # run pre-hook, and abort if it fails
514 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
515 pats=cmdpats, opts=cmdoptions)
516 if ret:
517 return ret
514 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
515 pats=cmdpats, opts=cmdoptions)
518 516 ret = _runcommand(ui, options, cmd, d)
519 517 # run post-hook, passing command result
520 518 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
521 519 result=ret, pats=cmdpats, opts=cmdoptions)
522 520 return ret
523 521
524 522 def _getlocal(ui, rpath):
525 523 """Return (path, local ui object) for the given target path.
526 524
527 525 Takes paths in [cwd]/.hg/hgrc into account."
528 526 """
529 527 try:
530 528 wd = os.getcwd()
531 529 except OSError, e:
532 530 raise util.Abort(_("error getting current working directory: %s") %
533 531 e.strerror)
534 532 path = cmdutil.findrepo(wd) or ""
535 533 if not path:
536 534 lui = ui
537 535 else:
538 536 lui = ui.copy()
539 537 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
540 538
541 539 if rpath and rpath[-1]:
542 540 path = lui.expandpath(rpath[-1])
543 541 lui = ui.copy()
544 542 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
545 543
546 544 return path, lui
547 545
548 546 def _checkshellalias(lui, ui, args):
549 547 options = {}
550 548
551 549 try:
552 550 args = fancyopts.fancyopts(args, commands.globalopts, options)
553 551 except fancyopts.getopt.GetoptError:
554 552 return
555 553
556 554 if not args:
557 555 return
558 556
559 557 norepo = commands.norepo
560 558 optionalrepo = commands.optionalrepo
561 559 def restorecommands():
562 560 commands.norepo = norepo
563 561 commands.optionalrepo = optionalrepo
564 562
565 563 cmdtable = commands.table.copy()
566 564 addaliases(lui, cmdtable)
567 565
568 566 cmd = args[0]
569 567 try:
570 568 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
571 569 lui.configbool("ui", "strict"))
572 570 except (error.AmbiguousCommand, error.UnknownCommand):
573 571 restorecommands()
574 572 return
575 573
576 574 cmd = aliases[0]
577 575 fn = entry[0]
578 576
579 577 if cmd and util.safehasattr(fn, 'shell'):
580 578 d = lambda: fn(ui, *args[1:])
581 579 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
582 580 [], {})
583 581
584 582 restorecommands()
585 583
586 584 _loaded = set()
587 585 def _dispatch(req):
588 586 args = req.args
589 587 ui = req.ui
590 588
591 589 # read --config before doing anything else
592 590 # (e.g. to change trust settings for reading .hg/hgrc)
593 591 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
594 592
595 593 # check for cwd
596 594 cwd = _earlygetopt(['--cwd'], args)
597 595 if cwd:
598 596 os.chdir(cwd[-1])
599 597
600 598 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
601 599 path, lui = _getlocal(ui, rpath)
602 600
603 601 # Now that we're operating in the right directory/repository with
604 602 # the right config settings, check for shell aliases
605 603 shellaliasfn = _checkshellalias(lui, ui, args)
606 604 if shellaliasfn:
607 605 return shellaliasfn()
608 606
609 607 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
610 608 # reposetup. Programs like TortoiseHg will call _dispatch several
611 609 # times so we keep track of configured extensions in _loaded.
612 610 extensions.loadall(lui)
613 611 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
614 612 # Propagate any changes to lui.__class__ by extensions
615 613 ui.__class__ = lui.__class__
616 614
617 615 # (uisetup and extsetup are handled in extensions.loadall)
618 616
619 617 for name, module in exts:
620 618 cmdtable = getattr(module, 'cmdtable', {})
621 619 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
622 620 if overrides:
623 621 ui.warn(_("extension '%s' overrides commands: %s\n")
624 622 % (name, " ".join(overrides)))
625 623 commands.table.update(cmdtable)
626 624 _loaded.add(name)
627 625
628 626 # (reposetup is handled in hg.repository)
629 627
630 628 addaliases(lui, commands.table)
631 629
632 630 # check for fallback encoding
633 631 fallback = lui.config('ui', 'fallbackencoding')
634 632 if fallback:
635 633 encoding.fallbackencoding = fallback
636 634
637 635 fullargs = args
638 636 cmd, func, args, options, cmdoptions = _parse(lui, args)
639 637
640 638 if options["config"]:
641 639 raise util.Abort(_("option --config may not be abbreviated!"))
642 640 if options["cwd"]:
643 641 raise util.Abort(_("option --cwd may not be abbreviated!"))
644 642 if options["repository"]:
645 643 raise util.Abort(_(
646 644 "option -R has to be separated from other options (e.g. not -qR) "
647 645 "and --repository may only be abbreviated as --repo!"))
648 646
649 647 if options["encoding"]:
650 648 encoding.encoding = options["encoding"]
651 649 if options["encodingmode"]:
652 650 encoding.encodingmode = options["encodingmode"]
653 651 if options["time"]:
654 652 def get_times():
655 653 t = os.times()
656 654 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
657 655 t = (t[0], t[1], t[2], t[3], time.clock())
658 656 return t
659 657 s = get_times()
660 658 def print_time():
661 659 t = get_times()
662 660 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
663 661 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
664 662 atexit.register(print_time)
665 663
666 664 uis = set([ui, lui])
667 665
668 666 if req.repo:
669 667 uis.add(req.repo.ui)
670 668
671 669 # copy configs that were passed on the cmdline (--config) to the repo ui
672 670 for cfg in cfgs:
673 671 req.repo.ui.setconfig(*cfg)
674 672
675 673 if options['verbose'] or options['debug'] or options['quiet']:
676 674 for opt in ('verbose', 'debug', 'quiet'):
677 675 val = str(bool(options[opt]))
678 676 for ui_ in uis:
679 677 ui_.setconfig('ui', opt, val)
680 678
681 679 if options['traceback']:
682 680 for ui_ in uis:
683 681 ui_.setconfig('ui', 'traceback', 'on')
684 682
685 683 if options['noninteractive']:
686 684 for ui_ in uis:
687 685 ui_.setconfig('ui', 'interactive', 'off')
688 686
689 687 if cmdoptions.get('insecure', False):
690 688 for ui_ in uis:
691 689 ui_.setconfig('web', 'cacerts', '')
692 690
693 691 if options['version']:
694 692 return commands.version_(ui)
695 693 if options['help']:
696 694 return commands.help_(ui, cmd)
697 695 elif not cmd:
698 696 return commands.help_(ui, 'shortlist')
699 697
700 698 repo = None
701 699 cmdpats = args[:]
702 700 if cmd not in commands.norepo.split():
703 701 # use the repo from the request only if we don't have -R
704 702 if not rpath and not cwd:
705 703 repo = req.repo
706 704
707 705 if repo:
708 706 # set the descriptors of the repo ui to those of ui
709 707 repo.ui.fin = ui.fin
710 708 repo.ui.fout = ui.fout
711 709 repo.ui.ferr = ui.ferr
712 710 else:
713 711 try:
714 712 repo = hg.repository(ui, path=path)
715 713 if not repo.local():
716 714 raise util.Abort(_("repository '%s' is not local") % path)
717 715 if options['hidden']:
718 716 repo = repo.unfiltered()
719 717 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
720 718 except error.RequirementError:
721 719 raise
722 720 except error.RepoError:
723 721 if cmd not in commands.optionalrepo.split():
724 722 if (cmd in commands.inferrepo.split() and
725 723 args and not path): # try to infer -R from command args
726 724 repos = map(cmdutil.findrepo, args)
727 725 guess = repos[0]
728 726 if guess and repos.count(guess) == len(repos):
729 727 req.args = ['--repository', guess] + fullargs
730 728 return _dispatch(req)
731 729 if not path:
732 730 raise error.RepoError(_("no repository found in '%s'"
733 731 " (.hg not found)")
734 732 % os.getcwd())
735 733 raise
736 734 if repo:
737 735 ui = repo.ui
738 736 args.insert(0, repo)
739 737 elif rpath:
740 738 ui.warn(_("warning: --repository ignored\n"))
741 739
742 740 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
743 741 ui.log("command", '%s\n', msg)
744 742 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
745 743 starttime = time.time()
746 744 ret = None
747 745 try:
748 746 ret = runcommand(lui, repo, cmd, fullargs, ui, options, d,
749 747 cmdpats, cmdoptions)
750 748 return ret
751 749 finally:
752 750 duration = time.time() - starttime
753 751 ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
754 752 cmd, ret, duration)
755 753 if repo and repo != req.repo:
756 754 repo.close()
757 755
758 756 def lsprofile(ui, func, fp):
759 757 format = ui.config('profiling', 'format', default='text')
760 758 field = ui.config('profiling', 'sort', default='inlinetime')
761 759 limit = ui.configint('profiling', 'limit', default=30)
762 760 climit = ui.configint('profiling', 'nested', default=5)
763 761
764 762 if format not in ['text', 'kcachegrind']:
765 763 ui.warn(_("unrecognized profiling format '%s'"
766 764 " - Ignored\n") % format)
767 765 format = 'text'
768 766
769 767 try:
770 768 from mercurial import lsprof
771 769 except ImportError:
772 770 raise util.Abort(_(
773 771 'lsprof not available - install from '
774 772 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
775 773 p = lsprof.Profiler()
776 774 p.enable(subcalls=True)
777 775 try:
778 776 return func()
779 777 finally:
780 778 p.disable()
781 779
782 780 if format == 'kcachegrind':
783 781 import lsprofcalltree
784 782 calltree = lsprofcalltree.KCacheGrind(p)
785 783 calltree.output(fp)
786 784 else:
787 785 # format == 'text'
788 786 stats = lsprof.Stats(p.getstats())
789 787 stats.sort(field)
790 788 stats.pprint(limit=limit, file=fp, climit=climit)
791 789
792 790 def statprofile(ui, func, fp):
793 791 try:
794 792 import statprof
795 793 except ImportError:
796 794 raise util.Abort(_(
797 795 'statprof not available - install using "easy_install statprof"'))
798 796
799 797 freq = ui.configint('profiling', 'freq', default=1000)
800 798 if freq > 0:
801 799 statprof.reset(freq)
802 800 else:
803 801 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
804 802
805 803 statprof.start()
806 804 try:
807 805 return func()
808 806 finally:
809 807 statprof.stop()
810 808 statprof.display(fp)
811 809
812 810 def _runcommand(ui, options, cmd, cmdfunc):
813 811 def checkargs():
814 812 try:
815 813 return cmdfunc()
816 814 except error.SignatureError:
817 815 raise error.CommandError(cmd, _("invalid arguments"))
818 816
819 817 if options['profile']:
820 818 profiler = os.getenv('HGPROF')
821 819 if profiler is None:
822 820 profiler = ui.config('profiling', 'type', default='ls')
823 821 if profiler not in ('ls', 'stat'):
824 822 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
825 823 profiler = 'ls'
826 824
827 825 output = ui.config('profiling', 'output')
828 826
829 827 if output:
830 828 path = ui.expandpath(output)
831 829 fp = open(path, 'wb')
832 830 else:
833 831 fp = sys.stderr
834 832
835 833 try:
836 834 if profiler == 'ls':
837 835 return lsprofile(ui, checkargs, fp)
838 836 else:
839 837 return statprofile(ui, checkargs, fp)
840 838 finally:
841 839 if output:
842 840 fp.close()
843 841 else:
844 842 return checkargs()
@@ -1,644 +1,644 b''
1 1 commit hooks can see env vars
2 2
3 3 $ hg init a
4 4 $ cd a
5 5 $ cat > .hg/hgrc <<EOF
6 6 > [hooks]
7 7 > commit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit"
8 8 > commit.b = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit.b"
9 9 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= python \"$TESTDIR/printenv.py\" precommit"
10 10 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" pretxncommit"
11 11 > pretxncommit.tip = hg -q tip
12 12 > pre-identify = python "$TESTDIR/printenv.py" pre-identify 1
13 13 > pre-cat = python "$TESTDIR/printenv.py" pre-cat
14 14 > post-cat = python "$TESTDIR/printenv.py" post-cat
15 15 > EOF
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 $ cat > .hg/hgrc <<EOF
33 33 > [hooks]
34 34 > prechangegroup = python "$TESTDIR/printenv.py" prechangegroup
35 35 > changegroup = python "$TESTDIR/printenv.py" changegroup
36 36 > incoming = python "$TESTDIR/printenv.py" incoming
37 37 > EOF
38 38
39 39 pretxncommit and commit hooks can see both parents of merge
40 40
41 41 $ cd ../a
42 42 $ echo b >> a
43 43 $ hg commit -m a1 -d "1 0"
44 44 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
45 45 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
46 46 1:ab228980c14d
47 47 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
48 48 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
49 49 $ hg update -C 0
50 50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 51 $ echo b > b
52 52 $ hg add b
53 53 $ hg commit -m b -d '1 0'
54 54 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
55 55 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
56 56 2:ee9deb46ab31
57 57 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
58 58 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
59 59 created new head
60 60 $ hg merge 1
61 61 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 62 (branch merge, don't forget to commit)
63 63 $ hg commit -m merge -d '2 0'
64 64 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
65 65 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
66 66 3:07f3376c1e65
67 67 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
68 68 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
69 69
70 70 test generic hooks
71 71
72 72 $ hg id
73 73 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=[]
74 warning: pre-identify hook exited with status 1
75 [1]
74 abort: pre-identify hook exited with status 1
75 [255]
76 76 $ hg cat b
77 77 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
78 78 b
79 79 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
80 80
81 81 $ cd ../b
82 82 $ hg pull ../a
83 83 pulling from ../a
84 84 searching for changes
85 85 prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
86 86 adding changesets
87 87 adding manifests
88 88 adding file changes
89 89 added 3 changesets with 2 changes to 2 files
90 90 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
91 91 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
92 92 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
93 93 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
94 94 (run 'hg update' to get a working copy)
95 95
96 96 tag hooks can see env vars
97 97
98 98 $ cd ../a
99 99 $ cat >> .hg/hgrc <<EOF
100 100 > pretag = python "$TESTDIR/printenv.py" pretag
101 101 > tag = sh -c "HG_PARENT1= HG_PARENT2= python \"$TESTDIR/printenv.py\" tag"
102 102 > EOF
103 103 $ hg tag -d '3 0' a
104 104 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
105 105 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
106 106 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
107 107 4:539e4b31b6dc
108 108 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
109 109 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
110 110 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
111 111 $ hg tag -l la
112 112 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
113 113 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
114 114
115 115 pretag hook can forbid tagging
116 116
117 117 $ echo "pretag.forbid = python \"$TESTDIR/printenv.py\" pretag.forbid 1" >> .hg/hgrc
118 118 $ hg tag -d '4 0' fa
119 119 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
120 120 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
121 121 abort: pretag.forbid hook exited with status 1
122 122 [255]
123 123 $ hg tag -l fla
124 124 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
125 125 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
126 126 abort: pretag.forbid hook exited with status 1
127 127 [255]
128 128
129 129 pretxncommit hook can see changeset, can roll back txn, changeset no
130 130 more there after
131 131
132 132 $ echo "pretxncommit.forbid0 = hg tip -q" >> .hg/hgrc
133 133 $ echo "pretxncommit.forbid1 = python \"$TESTDIR/printenv.py\" pretxncommit.forbid 1" >> .hg/hgrc
134 134 $ echo z > z
135 135 $ hg add z
136 136 $ hg -q tip
137 137 4:539e4b31b6dc
138 138 $ hg commit -m 'fail' -d '4 0'
139 139 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
140 140 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
141 141 5:6f611f8018c1
142 142 5:6f611f8018c1
143 143 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
144 144 transaction abort!
145 145 rollback completed
146 146 abort: pretxncommit.forbid1 hook exited with status 1
147 147 [255]
148 148 $ hg -q tip
149 149 4:539e4b31b6dc
150 150
151 151 precommit hook can prevent commit
152 152
153 153 $ echo "precommit.forbid = python \"$TESTDIR/printenv.py\" precommit.forbid 1" >> .hg/hgrc
154 154 $ hg commit -m 'fail' -d '4 0'
155 155 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
156 156 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
157 157 abort: precommit.forbid hook exited with status 1
158 158 [255]
159 159 $ hg -q tip
160 160 4:539e4b31b6dc
161 161
162 162 preupdate hook can prevent update
163 163
164 164 $ echo "preupdate = python \"$TESTDIR/printenv.py\" preupdate" >> .hg/hgrc
165 165 $ hg update 1
166 166 preupdate hook: HG_PARENT1=ab228980c14d
167 167 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
168 168
169 169 update hook
170 170
171 171 $ echo "update = python \"$TESTDIR/printenv.py\" update" >> .hg/hgrc
172 172 $ hg update
173 173 preupdate hook: HG_PARENT1=539e4b31b6dc
174 174 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
175 175 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 176
177 177 pushkey hook
178 178
179 179 $ echo "pushkey = python \"$TESTDIR/printenv.py\" pushkey" >> .hg/hgrc
180 180 $ cd ../b
181 181 $ hg bookmark -r null foo
182 182 $ hg push -B foo ../a
183 183 pushing to ../a
184 184 searching for changes
185 185 no changes found
186 186 exporting bookmark foo
187 187 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
188 188 [1]
189 189 $ cd ../a
190 190
191 191 listkeys hook
192 192
193 193 $ echo "listkeys = python \"$TESTDIR/printenv.py\" listkeys" >> .hg/hgrc
194 194 $ hg bookmark -r null bar
195 195 $ cd ../b
196 196 $ hg pull -B bar ../a
197 197 pulling from ../a
198 198 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
199 199 no changes found
200 200 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
201 201 adding remote bookmark bar
202 202 importing bookmark bar
203 203 $ cd ../a
204 204
205 205 test that prepushkey can prevent incoming keys
206 206
207 207 $ echo "prepushkey = python \"$TESTDIR/printenv.py\" prepushkey.forbid 1" >> .hg/hgrc
208 208 $ cd ../b
209 209 $ hg bookmark -r null baz
210 210 $ hg push -B baz ../a
211 211 pushing to ../a
212 212 searching for changes
213 213 no changes found
214 214 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
215 215 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
216 216 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
217 217 exporting bookmark baz
218 218 prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000
219 219 abort: prepushkey hook exited with status 1
220 220 [255]
221 221 $ cd ../a
222 222
223 223 test that prelistkeys can prevent listing keys
224 224
225 225 $ echo "prelistkeys = python \"$TESTDIR/printenv.py\" prelistkeys.forbid 1" >> .hg/hgrc
226 226 $ hg bookmark -r null quux
227 227 $ cd ../b
228 228 $ hg pull -B quux ../a
229 229 pulling from ../a
230 230 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
231 231 abort: prelistkeys hook exited with status 1
232 232 [255]
233 233 $ cd ../a
234 234 $ rm .hg/hgrc
235 235
236 236 prechangegroup hook can prevent incoming changes
237 237
238 238 $ cd ../b
239 239 $ hg -q tip
240 240 3:07f3376c1e65
241 241 $ cat > .hg/hgrc <<EOF
242 242 > [hooks]
243 243 > prechangegroup.forbid = python "$TESTDIR/printenv.py" prechangegroup.forbid 1
244 244 > EOF
245 245 $ hg pull ../a
246 246 pulling from ../a
247 247 searching for changes
248 248 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
249 249 abort: prechangegroup.forbid hook exited with status 1
250 250 [255]
251 251
252 252 pretxnchangegroup hook can see incoming changes, can roll back txn,
253 253 incoming changes no longer there after
254 254
255 255 $ cat > .hg/hgrc <<EOF
256 256 > [hooks]
257 257 > pretxnchangegroup.forbid0 = hg tip -q
258 258 > pretxnchangegroup.forbid1 = python "$TESTDIR/printenv.py" pretxnchangegroup.forbid 1
259 259 > EOF
260 260 $ hg pull ../a
261 261 pulling from ../a
262 262 searching for changes
263 263 adding changesets
264 264 adding manifests
265 265 adding file changes
266 266 added 1 changesets with 1 changes to 1 files
267 267 4:539e4b31b6dc
268 268 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a
269 269 transaction abort!
270 270 rollback completed
271 271 abort: pretxnchangegroup.forbid1 hook exited with status 1
272 272 [255]
273 273 $ hg -q tip
274 274 3:07f3376c1e65
275 275
276 276 outgoing hooks can see env vars
277 277
278 278 $ rm .hg/hgrc
279 279 $ cat > ../a/.hg/hgrc <<EOF
280 280 > [hooks]
281 281 > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing
282 282 > outgoing = python "$TESTDIR/printenv.py" outgoing
283 283 > EOF
284 284 $ hg pull ../a
285 285 pulling from ../a
286 286 searching for changes
287 287 preoutgoing hook: HG_SOURCE=pull
288 288 adding changesets
289 289 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
290 290 adding manifests
291 291 adding file changes
292 292 added 1 changesets with 1 changes to 1 files
293 293 adding remote bookmark quux
294 294 (run 'hg update' to get a working copy)
295 295 $ hg rollback
296 296 repository tip rolled back to revision 3 (undo pull)
297 297
298 298 preoutgoing hook can prevent outgoing changes
299 299
300 300 $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> ../a/.hg/hgrc
301 301 $ hg pull ../a
302 302 pulling from ../a
303 303 searching for changes
304 304 preoutgoing hook: HG_SOURCE=pull
305 305 preoutgoing.forbid hook: HG_SOURCE=pull
306 306 abort: preoutgoing.forbid hook exited with status 1
307 307 [255]
308 308
309 309 outgoing hooks work for local clones
310 310
311 311 $ cd ..
312 312 $ cat > a/.hg/hgrc <<EOF
313 313 > [hooks]
314 314 > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing
315 315 > outgoing = python "$TESTDIR/printenv.py" outgoing
316 316 > EOF
317 317 $ hg clone a c
318 318 preoutgoing hook: HG_SOURCE=clone
319 319 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
320 320 updating to branch default
321 321 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 322 $ rm -rf c
323 323
324 324 preoutgoing hook can prevent outgoing changes for local clones
325 325
326 326 $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> a/.hg/hgrc
327 327 $ hg clone a zzz
328 328 preoutgoing hook: HG_SOURCE=clone
329 329 preoutgoing.forbid hook: HG_SOURCE=clone
330 330 abort: preoutgoing.forbid hook exited with status 1
331 331 [255]
332 332
333 333 $ cd "$TESTTMP/b"
334 334
335 335 $ cat > hooktests.py <<EOF
336 336 > from mercurial import util
337 337 >
338 338 > uncallable = 0
339 339 >
340 340 > def printargs(args):
341 341 > args.pop('ui', None)
342 342 > args.pop('repo', None)
343 343 > a = list(args.items())
344 344 > a.sort()
345 345 > print 'hook args:'
346 346 > for k, v in a:
347 347 > print ' ', k, v
348 348 >
349 349 > def passhook(**args):
350 350 > printargs(args)
351 351 >
352 352 > def failhook(**args):
353 353 > printargs(args)
354 354 > return True
355 355 >
356 356 > class LocalException(Exception):
357 357 > pass
358 358 >
359 359 > def raisehook(**args):
360 360 > raise LocalException('exception from hook')
361 361 >
362 362 > def aborthook(**args):
363 363 > raise util.Abort('raise abort from hook')
364 364 >
365 365 > def brokenhook(**args):
366 366 > return 1 + {}
367 367 >
368 368 > def verbosehook(ui, **args):
369 369 > ui.note('verbose output from hook\n')
370 370 >
371 371 > def printtags(ui, repo, **args):
372 372 > print sorted(repo.tags())
373 373 >
374 374 > class container:
375 375 > unreachable = 1
376 376 > EOF
377 377
378 378 test python hooks
379 379
380 380 #if windows
381 381 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
382 382 #else
383 383 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
384 384 #endif
385 385 $ export PYTHONPATH
386 386
387 387 $ echo '[hooks]' > ../a/.hg/hgrc
388 388 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
389 389 $ hg pull ../a 2>&1 | grep 'raised an exception'
390 390 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
391 391
392 392 $ echo '[hooks]' > ../a/.hg/hgrc
393 393 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
394 394 $ hg pull ../a 2>&1 | grep 'raised an exception'
395 395 error: preoutgoing.raise hook raised an exception: exception from hook
396 396
397 397 $ echo '[hooks]' > ../a/.hg/hgrc
398 398 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
399 399 $ hg pull ../a
400 400 pulling from ../a
401 401 searching for changes
402 402 error: preoutgoing.abort hook failed: raise abort from hook
403 403 abort: raise abort from hook
404 404 [255]
405 405
406 406 $ echo '[hooks]' > ../a/.hg/hgrc
407 407 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
408 408 $ hg pull ../a
409 409 pulling from ../a
410 410 searching for changes
411 411 hook args:
412 412 hooktype preoutgoing
413 413 source pull
414 414 abort: preoutgoing.fail hook failed
415 415 [255]
416 416
417 417 $ echo '[hooks]' > ../a/.hg/hgrc
418 418 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
419 419 $ hg pull ../a
420 420 pulling from ../a
421 421 searching for changes
422 422 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
423 423 [255]
424 424
425 425 $ echo '[hooks]' > ../a/.hg/hgrc
426 426 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
427 427 $ hg pull ../a
428 428 pulling from ../a
429 429 searching for changes
430 430 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
431 431 [255]
432 432
433 433 $ echo '[hooks]' > ../a/.hg/hgrc
434 434 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
435 435 $ hg pull ../a
436 436 pulling from ../a
437 437 searching for changes
438 438 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
439 439 [255]
440 440
441 441 $ echo '[hooks]' > ../a/.hg/hgrc
442 442 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
443 443 $ hg pull ../a
444 444 pulling from ../a
445 445 searching for changes
446 446 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
447 447 [255]
448 448
449 449 $ echo '[hooks]' > ../a/.hg/hgrc
450 450 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
451 451 $ hg pull ../a
452 452 pulling from ../a
453 453 searching for changes
454 454 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
455 455 [255]
456 456
457 457 $ echo '[hooks]' > ../a/.hg/hgrc
458 458 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
459 459 $ hg pull ../a
460 460 pulling from ../a
461 461 searching for changes
462 462 hook args:
463 463 hooktype preoutgoing
464 464 source pull
465 465 adding changesets
466 466 adding manifests
467 467 adding file changes
468 468 added 1 changesets with 1 changes to 1 files
469 469 adding remote bookmark quux
470 470 (run 'hg update' to get a working copy)
471 471
472 472 make sure --traceback works
473 473
474 474 $ echo '[hooks]' > .hg/hgrc
475 475 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
476 476
477 477 $ echo aa > a
478 478 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
479 479 Traceback (most recent call last):
480 480
481 481 $ cd ..
482 482 $ hg init c
483 483 $ cd c
484 484
485 485 $ cat > hookext.py <<EOF
486 486 > def autohook(**args):
487 487 > print "Automatically installed hook"
488 488 >
489 489 > def reposetup(ui, repo):
490 490 > repo.ui.setconfig("hooks", "commit.auto", autohook)
491 491 > EOF
492 492 $ echo '[extensions]' >> .hg/hgrc
493 493 $ echo 'hookext = hookext.py' >> .hg/hgrc
494 494
495 495 $ touch foo
496 496 $ hg add foo
497 497 $ hg ci -d '0 0' -m 'add foo'
498 498 Automatically installed hook
499 499 $ echo >> foo
500 500 $ hg ci --debug -d '0 0' -m 'change foo'
501 501 foo
502 502 calling hook commit.auto: <function autohook at *> (glob)
503 503 Automatically installed hook
504 504 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
505 505
506 506 $ hg showconfig hooks
507 507 hooks.commit.auto=<function autohook at *> (glob)
508 508
509 509 test python hook configured with python:[file]:[hook] syntax
510 510
511 511 $ cd ..
512 512 $ mkdir d
513 513 $ cd d
514 514 $ hg init repo
515 515 $ mkdir hooks
516 516
517 517 $ cd hooks
518 518 $ cat > testhooks.py <<EOF
519 519 > def testhook(**args):
520 520 > print 'hook works'
521 521 > EOF
522 522 $ echo '[hooks]' > ../repo/.hg/hgrc
523 523 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
524 524
525 525 $ cd ../repo
526 526 $ hg commit -d '0 0'
527 527 hook works
528 528 nothing changed
529 529 [1]
530 530
531 531 $ echo '[hooks]' > .hg/hgrc
532 532 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
533 533 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
534 534
535 535 $ hg up null
536 536 loading update.ne hook failed:
537 537 abort: No such file or directory: $TESTTMP/d/repo/nonexistent.py
538 538 [255]
539 539
540 540 $ hg id
541 541 loading pre-identify.npmd hook failed:
542 542 abort: No module named repo!
543 543 [255]
544 544
545 545 $ cd ../../b
546 546
547 547 make sure --traceback works on hook import failure
548 548
549 549 $ cat > importfail.py <<EOF
550 550 > import somebogusmodule
551 551 > # dereference something in the module to force demandimport to load it
552 552 > somebogusmodule.whatever
553 553 > EOF
554 554
555 555 $ echo '[hooks]' > .hg/hgrc
556 556 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
557 557
558 558 $ echo a >> a
559 559 $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])'
560 560 exception from first failed import attempt:
561 561 Traceback (most recent call last):
562 562 ImportError: No module named somebogusmodule
563 563 exception from second failed import attempt:
564 564 Traceback (most recent call last):
565 565 ImportError: No module named hgext_importfail
566 566 Traceback (most recent call last):
567 567 Abort: precommit.importfail hook is invalid (import of "importfail" failed)
568 568 abort: precommit.importfail hook is invalid (import of "importfail" failed)
569 569
570 570 Issue1827: Hooks Update & Commit not completely post operation
571 571
572 572 commit and update hooks should run after command completion
573 573
574 574 $ echo '[hooks]' > .hg/hgrc
575 575 $ echo 'commit = hg id' >> .hg/hgrc
576 576 $ echo 'update = hg id' >> .hg/hgrc
577 577 $ echo bb > a
578 578 $ hg ci -ma
579 579 223eafe2750c tip
580 580 $ hg up 0
581 581 cb9a9f314b8b
582 582 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
583 583
584 584 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
585 585 that is passed to pre/post hooks
586 586
587 587 $ echo '[hooks]' > .hg/hgrc
588 588 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
589 589 $ hg id
590 590 cb9a9f314b8b
591 591 $ hg id --verbose
592 592 calling hook pre-identify: hooktests.verbosehook
593 593 verbose output from hook
594 594 cb9a9f314b8b
595 595
596 596 Ensure hooks can be prioritized
597 597
598 598 $ echo '[hooks]' > .hg/hgrc
599 599 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
600 600 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
601 601 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
602 602 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
603 603 $ hg id --verbose
604 604 calling hook pre-identify.b: hooktests.verbosehook
605 605 verbose output from hook
606 606 calling hook pre-identify.a: hooktests.verbosehook
607 607 verbose output from hook
608 608 calling hook pre-identify.c: hooktests.verbosehook
609 609 verbose output from hook
610 610 cb9a9f314b8b
611 611
612 612 new tags must be visible in pretxncommit (issue3210)
613 613
614 614 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
615 615 $ hg tag -f foo
616 616 ['a', 'foo', 'tip']
617 617
618 618 new commits must be visible in pretxnchangegroup (issue3428)
619 619
620 620 $ cd ..
621 621 $ hg init to
622 622 $ echo '[hooks]' >> to/.hg/hgrc
623 623 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
624 624 $ echo a >> to/a
625 625 $ hg --cwd to ci -Ama
626 626 adding a
627 627 $ hg clone to from
628 628 updating to branch default
629 629 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
630 630 $ echo aa >> from/a
631 631 $ hg --cwd from ci -mb
632 632 $ hg --cwd from push
633 633 pushing to $TESTTMP/to (glob)
634 634 searching for changes
635 635 adding changesets
636 636 adding manifests
637 637 adding file changes
638 638 added 1 changesets with 1 changes to 1 files
639 639 changeset: 1:9836a07b9b9d
640 640 tag: tip
641 641 user: test
642 642 date: Thu Jan 01 00:00:00 1970 +0000
643 643 summary: b
644 644
General Comments 0
You need to be logged in to leave comments. Login now