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