##// END OF EJS Templates
hooks: fix pre- and post- hooks specified in .hg/hgrc...
Matt Mackall -
r5869:2c565b95 default
parent child Browse files
Show More
@@ -1,413 +1,413 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex, time
10 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex, time
11 import util, commands, hg, lock, fancyopts, revlog, version, extensions, hook
11 import util, commands, hg, lock, fancyopts, revlog, version, extensions, hook
12 import cmdutil
12 import cmdutil
13 import ui as _ui
13 import ui as _ui
14
14
15 class ParseError(Exception):
15 class ParseError(Exception):
16 """Exception raised on errors in parsing the command line."""
16 """Exception raised on errors in parsing the command line."""
17
17
18 def run():
18 def run():
19 "run the command in sys.argv"
19 "run the command in sys.argv"
20 sys.exit(dispatch(sys.argv[1:]))
20 sys.exit(dispatch(sys.argv[1:]))
21
21
22 def dispatch(args):
22 def dispatch(args):
23 "run the command specified in args"
23 "run the command specified in args"
24 try:
24 try:
25 u = _ui.ui(traceback='--traceback' in args)
25 u = _ui.ui(traceback='--traceback' in args)
26 except util.Abort, inst:
26 except util.Abort, inst:
27 sys.stderr.write(_("abort: %s\n") % inst)
27 sys.stderr.write(_("abort: %s\n") % inst)
28 return -1
28 return -1
29 return _runcatch(u, args)
29 return _runcatch(u, args)
30
30
31 def _runcatch(ui, args):
31 def _runcatch(ui, args):
32 def catchterm(*args):
32 def catchterm(*args):
33 raise util.SignalInterrupt
33 raise util.SignalInterrupt
34
34
35 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
35 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
36 num = getattr(signal, name, None)
36 num = getattr(signal, name, None)
37 if num: signal.signal(num, catchterm)
37 if num: signal.signal(num, catchterm)
38
38
39 try:
39 try:
40 try:
40 try:
41 # enter the debugger before command execution
41 # enter the debugger before command execution
42 if '--debugger' in args:
42 if '--debugger' in args:
43 pdb.set_trace()
43 pdb.set_trace()
44 try:
44 try:
45 return _dispatch(ui, args)
45 return _dispatch(ui, args)
46 finally:
46 finally:
47 ui.flush()
47 ui.flush()
48 except:
48 except:
49 # enter the debugger when we hit an exception
49 # enter the debugger when we hit an exception
50 if '--debugger' in args:
50 if '--debugger' in args:
51 pdb.post_mortem(sys.exc_info()[2])
51 pdb.post_mortem(sys.exc_info()[2])
52 ui.print_exc()
52 ui.print_exc()
53 raise
53 raise
54
54
55 except ParseError, inst:
55 except ParseError, inst:
56 if inst.args[0]:
56 if inst.args[0]:
57 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
57 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
58 commands.help_(ui, inst.args[0])
58 commands.help_(ui, inst.args[0])
59 else:
59 else:
60 ui.warn(_("hg: %s\n") % inst.args[1])
60 ui.warn(_("hg: %s\n") % inst.args[1])
61 commands.help_(ui, 'shortlist')
61 commands.help_(ui, 'shortlist')
62 except cmdutil.AmbiguousCommand, inst:
62 except cmdutil.AmbiguousCommand, inst:
63 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
63 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
64 (inst.args[0], " ".join(inst.args[1])))
64 (inst.args[0], " ".join(inst.args[1])))
65 except cmdutil.UnknownCommand, inst:
65 except cmdutil.UnknownCommand, inst:
66 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
66 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
67 commands.help_(ui, 'shortlist')
67 commands.help_(ui, 'shortlist')
68 except hg.RepoError, inst:
68 except hg.RepoError, inst:
69 ui.warn(_("abort: %s!\n") % inst)
69 ui.warn(_("abort: %s!\n") % inst)
70 except lock.LockHeld, inst:
70 except lock.LockHeld, inst:
71 if inst.errno == errno.ETIMEDOUT:
71 if inst.errno == errno.ETIMEDOUT:
72 reason = _('timed out waiting for lock held by %s') % inst.locker
72 reason = _('timed out waiting for lock held by %s') % inst.locker
73 else:
73 else:
74 reason = _('lock held by %s') % inst.locker
74 reason = _('lock held by %s') % inst.locker
75 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
75 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
76 except lock.LockUnavailable, inst:
76 except lock.LockUnavailable, inst:
77 ui.warn(_("abort: could not lock %s: %s\n") %
77 ui.warn(_("abort: could not lock %s: %s\n") %
78 (inst.desc or inst.filename, inst.strerror))
78 (inst.desc or inst.filename, inst.strerror))
79 except revlog.RevlogError, inst:
79 except revlog.RevlogError, inst:
80 ui.warn(_("abort: %s!\n") % inst)
80 ui.warn(_("abort: %s!\n") % inst)
81 except util.SignalInterrupt:
81 except util.SignalInterrupt:
82 ui.warn(_("killed!\n"))
82 ui.warn(_("killed!\n"))
83 except KeyboardInterrupt:
83 except KeyboardInterrupt:
84 try:
84 try:
85 ui.warn(_("interrupted!\n"))
85 ui.warn(_("interrupted!\n"))
86 except IOError, inst:
86 except IOError, inst:
87 if inst.errno == errno.EPIPE:
87 if inst.errno == errno.EPIPE:
88 if ui.debugflag:
88 if ui.debugflag:
89 ui.warn(_("\nbroken pipe\n"))
89 ui.warn(_("\nbroken pipe\n"))
90 else:
90 else:
91 raise
91 raise
92 except socket.error, inst:
92 except socket.error, inst:
93 ui.warn(_("abort: %s\n") % inst[1])
93 ui.warn(_("abort: %s\n") % inst[1])
94 except IOError, inst:
94 except IOError, inst:
95 if hasattr(inst, "code"):
95 if hasattr(inst, "code"):
96 ui.warn(_("abort: %s\n") % inst)
96 ui.warn(_("abort: %s\n") % inst)
97 elif hasattr(inst, "reason"):
97 elif hasattr(inst, "reason"):
98 try: # usually it is in the form (errno, strerror)
98 try: # usually it is in the form (errno, strerror)
99 reason = inst.reason.args[1]
99 reason = inst.reason.args[1]
100 except: # it might be anything, for example a string
100 except: # it might be anything, for example a string
101 reason = inst.reason
101 reason = inst.reason
102 ui.warn(_("abort: error: %s\n") % reason)
102 ui.warn(_("abort: error: %s\n") % reason)
103 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
103 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
104 if ui.debugflag:
104 if ui.debugflag:
105 ui.warn(_("broken pipe\n"))
105 ui.warn(_("broken pipe\n"))
106 elif getattr(inst, "strerror", None):
106 elif getattr(inst, "strerror", None):
107 if getattr(inst, "filename", None):
107 if getattr(inst, "filename", None):
108 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
108 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
109 else:
109 else:
110 ui.warn(_("abort: %s\n") % inst.strerror)
110 ui.warn(_("abort: %s\n") % inst.strerror)
111 else:
111 else:
112 raise
112 raise
113 except OSError, inst:
113 except OSError, inst:
114 if getattr(inst, "filename", None):
114 if getattr(inst, "filename", None):
115 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
115 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
116 else:
116 else:
117 ui.warn(_("abort: %s\n") % inst.strerror)
117 ui.warn(_("abort: %s\n") % inst.strerror)
118 except util.UnexpectedOutput, inst:
118 except util.UnexpectedOutput, inst:
119 ui.warn(_("abort: %s") % inst[0])
119 ui.warn(_("abort: %s") % inst[0])
120 if not isinstance(inst[1], basestring):
120 if not isinstance(inst[1], basestring):
121 ui.warn(" %r\n" % (inst[1],))
121 ui.warn(" %r\n" % (inst[1],))
122 elif not inst[1]:
122 elif not inst[1]:
123 ui.warn(_(" empty string\n"))
123 ui.warn(_(" empty string\n"))
124 else:
124 else:
125 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
125 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
126 except ImportError, inst:
126 except ImportError, inst:
127 m = str(inst).split()[-1]
127 m = str(inst).split()[-1]
128 ui.warn(_("abort: could not import module %s!\n") % m)
128 ui.warn(_("abort: could not import module %s!\n") % m)
129 if m in "mpatch bdiff".split():
129 if m in "mpatch bdiff".split():
130 ui.warn(_("(did you forget to compile extensions?)\n"))
130 ui.warn(_("(did you forget to compile extensions?)\n"))
131 elif m in "zlib".split():
131 elif m in "zlib".split():
132 ui.warn(_("(is your Python install correct?)\n"))
132 ui.warn(_("(is your Python install correct?)\n"))
133
133
134 except util.Abort, inst:
134 except util.Abort, inst:
135 ui.warn(_("abort: %s\n") % inst)
135 ui.warn(_("abort: %s\n") % inst)
136 except MemoryError:
136 except MemoryError:
137 ui.warn(_("abort: out of memory\n"))
137 ui.warn(_("abort: out of memory\n"))
138 except SystemExit, inst:
138 except SystemExit, inst:
139 # Commands shouldn't sys.exit directly, but give a return code.
139 # Commands shouldn't sys.exit directly, but give a return code.
140 # Just in case catch this and and pass exit code to caller.
140 # Just in case catch this and and pass exit code to caller.
141 return inst.code
141 return inst.code
142 except:
142 except:
143 ui.warn(_("** unknown exception encountered, details follow\n"))
143 ui.warn(_("** unknown exception encountered, details follow\n"))
144 ui.warn(_("** report bug details to "
144 ui.warn(_("** report bug details to "
145 "http://www.selenic.com/mercurial/bts\n"))
145 "http://www.selenic.com/mercurial/bts\n"))
146 ui.warn(_("** or mercurial@selenic.com\n"))
146 ui.warn(_("** or mercurial@selenic.com\n"))
147 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
147 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
148 % version.get_version())
148 % version.get_version())
149 raise
149 raise
150
150
151 return -1
151 return -1
152
152
153 def _findrepo():
153 def _findrepo():
154 p = os.getcwd()
154 p = os.getcwd()
155 while not os.path.isdir(os.path.join(p, ".hg")):
155 while not os.path.isdir(os.path.join(p, ".hg")):
156 oldp, p = p, os.path.dirname(p)
156 oldp, p = p, os.path.dirname(p)
157 if p == oldp:
157 if p == oldp:
158 return None
158 return None
159
159
160 return p
160 return p
161
161
162 def _parse(ui, args):
162 def _parse(ui, args):
163 options = {}
163 options = {}
164 cmdoptions = {}
164 cmdoptions = {}
165
165
166 try:
166 try:
167 args = fancyopts.fancyopts(args, commands.globalopts, options)
167 args = fancyopts.fancyopts(args, commands.globalopts, options)
168 except fancyopts.getopt.GetoptError, inst:
168 except fancyopts.getopt.GetoptError, inst:
169 raise ParseError(None, inst)
169 raise ParseError(None, inst)
170
170
171 if args:
171 if args:
172 cmd, args = args[0], args[1:]
172 cmd, args = args[0], args[1:]
173 aliases, i = cmdutil.findcmd(ui, cmd, commands.table)
173 aliases, i = cmdutil.findcmd(ui, cmd, commands.table)
174 cmd = aliases[0]
174 cmd = aliases[0]
175 defaults = ui.config("defaults", cmd)
175 defaults = ui.config("defaults", cmd)
176 if defaults:
176 if defaults:
177 args = shlex.split(defaults) + args
177 args = shlex.split(defaults) + args
178 c = list(i[1])
178 c = list(i[1])
179 else:
179 else:
180 cmd = None
180 cmd = None
181 c = []
181 c = []
182
182
183 # combine global options into local
183 # combine global options into local
184 for o in commands.globalopts:
184 for o in commands.globalopts:
185 c.append((o[0], o[1], options[o[1]], o[3]))
185 c.append((o[0], o[1], options[o[1]], o[3]))
186
186
187 try:
187 try:
188 args = fancyopts.fancyopts(args, c, cmdoptions)
188 args = fancyopts.fancyopts(args, c, cmdoptions)
189 except fancyopts.getopt.GetoptError, inst:
189 except fancyopts.getopt.GetoptError, inst:
190 raise ParseError(cmd, inst)
190 raise ParseError(cmd, inst)
191
191
192 # separate global options back out
192 # separate global options back out
193 for o in commands.globalopts:
193 for o in commands.globalopts:
194 n = o[1]
194 n = o[1]
195 options[n] = cmdoptions[n]
195 options[n] = cmdoptions[n]
196 del cmdoptions[n]
196 del cmdoptions[n]
197
197
198 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
198 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
199
199
200 def _parseconfig(config):
200 def _parseconfig(config):
201 """parse the --config options from the command line"""
201 """parse the --config options from the command line"""
202 parsed = []
202 parsed = []
203 for cfg in config:
203 for cfg in config:
204 try:
204 try:
205 name, value = cfg.split('=', 1)
205 name, value = cfg.split('=', 1)
206 section, name = name.split('.', 1)
206 section, name = name.split('.', 1)
207 if not section or not name:
207 if not section or not name:
208 raise IndexError
208 raise IndexError
209 parsed.append((section, name, value))
209 parsed.append((section, name, value))
210 except (IndexError, ValueError):
210 except (IndexError, ValueError):
211 raise util.Abort(_('malformed --config option: %s') % cfg)
211 raise util.Abort(_('malformed --config option: %s') % cfg)
212 return parsed
212 return parsed
213
213
214 def _earlygetopt(aliases, args):
214 def _earlygetopt(aliases, args):
215 """Return list of values for an option (or aliases).
215 """Return list of values for an option (or aliases).
216
216
217 The values are listed in the order they appear in args.
217 The values are listed in the order they appear in args.
218 The options and values are removed from args.
218 The options and values are removed from args.
219 """
219 """
220 try:
220 try:
221 argcount = args.index("--")
221 argcount = args.index("--")
222 except ValueError:
222 except ValueError:
223 argcount = len(args)
223 argcount = len(args)
224 shortopts = [opt for opt in aliases if len(opt) == 2]
224 shortopts = [opt for opt in aliases if len(opt) == 2]
225 values = []
225 values = []
226 pos = 0
226 pos = 0
227 while pos < argcount:
227 while pos < argcount:
228 if args[pos] in aliases:
228 if args[pos] in aliases:
229 if pos + 1 >= argcount:
229 if pos + 1 >= argcount:
230 # ignore and let getopt report an error if there is no value
230 # ignore and let getopt report an error if there is no value
231 break
231 break
232 del args[pos]
232 del args[pos]
233 values.append(args.pop(pos))
233 values.append(args.pop(pos))
234 argcount -= 2
234 argcount -= 2
235 elif args[pos][:2] in shortopts:
235 elif args[pos][:2] in shortopts:
236 # short option can have no following space, e.g. hg log -Rfoo
236 # short option can have no following space, e.g. hg log -Rfoo
237 values.append(args.pop(pos)[2:])
237 values.append(args.pop(pos)[2:])
238 argcount -= 1
238 argcount -= 1
239 else:
239 else:
240 pos += 1
240 pos += 1
241 return values
241 return values
242
242
243 _loaded = {}
243 _loaded = {}
244 def _dispatch(ui, args):
244 def _dispatch(ui, args):
245 # read --config before doing anything else
245 # read --config before doing anything else
246 # (e.g. to change trust settings for reading .hg/hgrc)
246 # (e.g. to change trust settings for reading .hg/hgrc)
247 config = _earlygetopt(['--config'], args)
247 config = _earlygetopt(['--config'], args)
248 if config:
248 if config:
249 ui.updateopts(config=_parseconfig(config))
249 ui.updateopts(config=_parseconfig(config))
250
250
251 # check for cwd
251 # check for cwd
252 cwd = _earlygetopt(['--cwd'], args)
252 cwd = _earlygetopt(['--cwd'], args)
253 if cwd:
253 if cwd:
254 os.chdir(cwd[-1])
254 os.chdir(cwd[-1])
255
255
256 # read the local repository .hgrc into a local ui object
256 # read the local repository .hgrc into a local ui object
257 path = _findrepo() or ""
257 path = _findrepo() or ""
258 if not path:
258 if not path:
259 lui = ui
259 lui = ui
260 if path:
260 if path:
261 try:
261 try:
262 lui = _ui.ui(parentui=ui)
262 lui = _ui.ui(parentui=ui)
263 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
263 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
264 except IOError:
264 except IOError:
265 pass
265 pass
266
266
267 # now we can expand paths, even ones in .hg/hgrc
267 # now we can expand paths, even ones in .hg/hgrc
268 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
268 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
269 if rpath:
269 if rpath:
270 path = lui.expandpath(rpath[-1])
270 path = lui.expandpath(rpath[-1])
271 lui = _ui.ui(parentui=ui)
271 lui = _ui.ui(parentui=ui)
272 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
272 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
273
273
274 extensions.loadall(lui)
274 extensions.loadall(lui)
275 for name, module in extensions.extensions():
275 for name, module in extensions.extensions():
276 if name in _loaded:
276 if name in _loaded:
277 continue
277 continue
278
278
279 # setup extensions
279 # setup extensions
280 # TODO this should be generalized to scheme, where extensions can
280 # TODO this should be generalized to scheme, where extensions can
281 # redepend on other extensions. then we should toposort them, and
281 # redepend on other extensions. then we should toposort them, and
282 # do initialization in correct order
282 # do initialization in correct order
283 extsetup = getattr(module, 'extsetup', None)
283 extsetup = getattr(module, 'extsetup', None)
284 if extsetup:
284 if extsetup:
285 extsetup()
285 extsetup()
286
286
287 cmdtable = getattr(module, 'cmdtable', {})
287 cmdtable = getattr(module, 'cmdtable', {})
288 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
288 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
289 if overrides:
289 if overrides:
290 ui.warn(_("extension '%s' overrides commands: %s\n")
290 ui.warn(_("extension '%s' overrides commands: %s\n")
291 % (name, " ".join(overrides)))
291 % (name, " ".join(overrides)))
292 commands.table.update(cmdtable)
292 commands.table.update(cmdtable)
293 _loaded[name] = 1
293 _loaded[name] = 1
294 # check for fallback encoding
294 # check for fallback encoding
295 fallback = lui.config('ui', 'fallbackencoding')
295 fallback = lui.config('ui', 'fallbackencoding')
296 if fallback:
296 if fallback:
297 util._fallbackencoding = fallback
297 util._fallbackencoding = fallback
298
298
299 fullargs = args
299 fullargs = args
300 cmd, func, args, options, cmdoptions = _parse(lui, args)
300 cmd, func, args, options, cmdoptions = _parse(lui, args)
301
301
302 if options["config"]:
302 if options["config"]:
303 raise util.Abort(_("Option --config may not be abbreviated!"))
303 raise util.Abort(_("Option --config may not be abbreviated!"))
304 if options["cwd"]:
304 if options["cwd"]:
305 raise util.Abort(_("Option --cwd may not be abbreviated!"))
305 raise util.Abort(_("Option --cwd may not be abbreviated!"))
306 if options["repository"]:
306 if options["repository"]:
307 raise util.Abort(_(
307 raise util.Abort(_(
308 "Option -R has to be separated from other options (i.e. not -qR) "
308 "Option -R has to be separated from other options (i.e. not -qR) "
309 "and --repository may only be abbreviated as --repo!"))
309 "and --repository may only be abbreviated as --repo!"))
310
310
311 if options["encoding"]:
311 if options["encoding"]:
312 util._encoding = options["encoding"]
312 util._encoding = options["encoding"]
313 if options["encodingmode"]:
313 if options["encodingmode"]:
314 util._encodingmode = options["encodingmode"]
314 util._encodingmode = options["encodingmode"]
315 if options["time"]:
315 if options["time"]:
316 def get_times():
316 def get_times():
317 t = os.times()
317 t = os.times()
318 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
318 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
319 t = (t[0], t[1], t[2], t[3], time.clock())
319 t = (t[0], t[1], t[2], t[3], time.clock())
320 return t
320 return t
321 s = get_times()
321 s = get_times()
322 def print_time():
322 def print_time():
323 t = get_times()
323 t = get_times()
324 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
324 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
325 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
325 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
326 atexit.register(print_time)
326 atexit.register(print_time)
327
327
328 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
328 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
329 not options["noninteractive"], options["traceback"])
329 not options["noninteractive"], options["traceback"])
330
330
331 if options['help']:
331 if options['help']:
332 return commands.help_(ui, cmd, options['version'])
332 return commands.help_(ui, cmd, options['version'])
333 elif options['version']:
333 elif options['version']:
334 return commands.version_(ui)
334 return commands.version_(ui)
335 elif not cmd:
335 elif not cmd:
336 return commands.help_(ui, 'shortlist')
336 return commands.help_(ui, 'shortlist')
337
337
338 repo = None
338 repo = None
339 if cmd not in commands.norepo.split():
339 if cmd not in commands.norepo.split():
340 try:
340 try:
341 repo = hg.repository(ui, path=path)
341 repo = hg.repository(ui, path=path)
342 ui = repo.ui
342 ui = repo.ui
343 ui.setconfig("bundle", "mainreporoot", repo.root)
343 ui.setconfig("bundle", "mainreporoot", repo.root)
344 if not repo.local():
344 if not repo.local():
345 raise util.Abort(_("repository '%s' is not local") % path)
345 raise util.Abort(_("repository '%s' is not local") % path)
346 except hg.RepoError:
346 except hg.RepoError:
347 if cmd not in commands.optionalrepo.split():
347 if cmd not in commands.optionalrepo.split():
348 if not path:
348 if not path:
349 raise hg.RepoError(_("There is no Mercurial repository here"
349 raise hg.RepoError(_("There is no Mercurial repository here"
350 " (.hg not found)"))
350 " (.hg not found)"))
351 raise
351 raise
352 d = lambda: func(ui, repo, *args, **cmdoptions)
352 d = lambda: func(ui, repo, *args, **cmdoptions)
353 else:
353 else:
354 d = lambda: func(ui, *args, **cmdoptions)
354 d = lambda: func(ui, *args, **cmdoptions)
355
355
356 # run pre-hook, and abort if it fails
356 # run pre-hook, and abort if it fails
357 ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
357 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
358 if ret:
358 if ret:
359 return ret
359 return ret
360 ret = _runcommand(ui, options, cmd, d)
360 ret = _runcommand(ui, options, cmd, d)
361 # run post-hook, passing command result
361 # run post-hook, passing command result
362 hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
362 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
363 result = ret)
363 result = ret)
364 return ret
364 return ret
365
365
366 def _runcommand(ui, options, cmd, cmdfunc):
366 def _runcommand(ui, options, cmd, cmdfunc):
367 def checkargs():
367 def checkargs():
368 try:
368 try:
369 return cmdfunc()
369 return cmdfunc()
370 except TypeError, inst:
370 except TypeError, inst:
371 # was this an argument error?
371 # was this an argument error?
372 tb = traceback.extract_tb(sys.exc_info()[2])
372 tb = traceback.extract_tb(sys.exc_info()[2])
373 if len(tb) != 2: # no
373 if len(tb) != 2: # no
374 raise
374 raise
375 raise ParseError(cmd, _("invalid arguments"))
375 raise ParseError(cmd, _("invalid arguments"))
376
376
377 if options['profile']:
377 if options['profile']:
378 import hotshot, hotshot.stats
378 import hotshot, hotshot.stats
379 prof = hotshot.Profile("hg.prof")
379 prof = hotshot.Profile("hg.prof")
380 try:
380 try:
381 try:
381 try:
382 return prof.runcall(checkargs)
382 return prof.runcall(checkargs)
383 except:
383 except:
384 try:
384 try:
385 ui.warn(_('exception raised - generating '
385 ui.warn(_('exception raised - generating '
386 'profile anyway\n'))
386 'profile anyway\n'))
387 except:
387 except:
388 pass
388 pass
389 raise
389 raise
390 finally:
390 finally:
391 prof.close()
391 prof.close()
392 stats = hotshot.stats.load("hg.prof")
392 stats = hotshot.stats.load("hg.prof")
393 stats.strip_dirs()
393 stats.strip_dirs()
394 stats.sort_stats('time', 'calls')
394 stats.sort_stats('time', 'calls')
395 stats.print_stats(40)
395 stats.print_stats(40)
396 elif options['lsprof']:
396 elif options['lsprof']:
397 try:
397 try:
398 from mercurial import lsprof
398 from mercurial import lsprof
399 except ImportError:
399 except ImportError:
400 raise util.Abort(_(
400 raise util.Abort(_(
401 'lsprof not available - install from '
401 'lsprof not available - install from '
402 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
402 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
403 p = lsprof.Profiler()
403 p = lsprof.Profiler()
404 p.enable(subcalls=True)
404 p.enable(subcalls=True)
405 try:
405 try:
406 return checkargs()
406 return checkargs()
407 finally:
407 finally:
408 p.disable()
408 p.disable()
409 stats = lsprof.Stats(p.getstats())
409 stats = lsprof.Stats(p.getstats())
410 stats.sort()
410 stats.sort()
411 stats.pprint(top=10, file=sys.stderr, climit=5)
411 stats.pprint(top=10, file=sys.stderr, climit=5)
412 else:
412 else:
413 return checkargs()
413 return checkargs()
@@ -1,109 +1,113 b''
1 # hook.py - hook support for mercurial
1 # hook.py - hook support for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import util, os, sys
9 import util, os, sys
10
10
11 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
11 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
12 '''call python hook. hook is callable object, looked up as
12 '''call python hook. hook is callable object, looked up as
13 name in python module. if callable returns "true", hook
13 name in python module. if callable returns "true", hook
14 fails, else passes. if hook raises exception, treated as
14 fails, else passes. if hook raises exception, treated as
15 hook failure. exception propagates if throw is "true".
15 hook failure. exception propagates if throw is "true".
16
16
17 reason for "true" meaning "hook failed" is so that
17 reason for "true" meaning "hook failed" is so that
18 unmodified commands (e.g. mercurial.commands.update) can
18 unmodified commands (e.g. mercurial.commands.update) can
19 be run as hooks without wrappers to convert return values.'''
19 be run as hooks without wrappers to convert return values.'''
20
20
21 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
21 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
22 obj = funcname
22 obj = funcname
23 if not callable(obj):
23 if not callable(obj):
24 d = funcname.rfind('.')
24 d = funcname.rfind('.')
25 if d == -1:
25 if d == -1:
26 raise util.Abort(_('%s hook is invalid ("%s" not in '
26 raise util.Abort(_('%s hook is invalid ("%s" not in '
27 'a module)') % (hname, funcname))
27 'a module)') % (hname, funcname))
28 modname = funcname[:d]
28 modname = funcname[:d]
29 try:
29 try:
30 obj = __import__(modname)
30 obj = __import__(modname)
31 except ImportError:
31 except ImportError:
32 try:
32 try:
33 # extensions are loaded with hgext_ prefix
33 # extensions are loaded with hgext_ prefix
34 obj = __import__("hgext_%s" % modname)
34 obj = __import__("hgext_%s" % modname)
35 except ImportError:
35 except ImportError:
36 raise util.Abort(_('%s hook is invalid '
36 raise util.Abort(_('%s hook is invalid '
37 '(import of "%s" failed)') %
37 '(import of "%s" failed)') %
38 (hname, modname))
38 (hname, modname))
39 try:
39 try:
40 for p in funcname.split('.')[1:]:
40 for p in funcname.split('.')[1:]:
41 obj = getattr(obj, p)
41 obj = getattr(obj, p)
42 except AttributeError, err:
42 except AttributeError, err:
43 raise util.Abort(_('%s hook is invalid '
43 raise util.Abort(_('%s hook is invalid '
44 '("%s" is not defined)') %
44 '("%s" is not defined)') %
45 (hname, funcname))
45 (hname, funcname))
46 if not callable(obj):
46 if not callable(obj):
47 raise util.Abort(_('%s hook is invalid '
47 raise util.Abort(_('%s hook is invalid '
48 '("%s" is not callable)') %
48 '("%s" is not callable)') %
49 (hname, funcname))
49 (hname, funcname))
50 try:
50 try:
51 r = obj(ui=ui, repo=repo, hooktype=name, **args)
51 r = obj(ui=ui, repo=repo, hooktype=name, **args)
52 except (KeyboardInterrupt, util.SignalInterrupt):
52 except (KeyboardInterrupt, util.SignalInterrupt):
53 raise
53 raise
54 except Exception, exc:
54 except Exception, exc:
55 if isinstance(exc, util.Abort):
55 if isinstance(exc, util.Abort):
56 ui.warn(_('error: %s hook failed: %s\n') %
56 ui.warn(_('error: %s hook failed: %s\n') %
57 (hname, exc.args[0]))
57 (hname, exc.args[0]))
58 else:
58 else:
59 ui.warn(_('error: %s hook raised an exception: '
59 ui.warn(_('error: %s hook raised an exception: '
60 '%s\n') % (hname, exc))
60 '%s\n') % (hname, exc))
61 if throw:
61 if throw:
62 raise
62 raise
63 ui.print_exc()
63 ui.print_exc()
64 return True
64 return True
65 if r:
65 if r:
66 if throw:
66 if throw:
67 raise util.Abort(_('%s hook failed') % hname)
67 raise util.Abort(_('%s hook failed') % hname)
68 ui.warn(_('warning: %s hook failed\n') % hname)
68 ui.warn(_('warning: %s hook failed\n') % hname)
69 return r
69 return r
70
70
71 def _exthook(ui, repo, name, cmd, args, throw):
71 def _exthook(ui, repo, name, cmd, args, throw):
72 ui.note(_("running hook %s: %s\n") % (name, cmd))
72 ui.note(_("running hook %s: %s\n") % (name, cmd))
73 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
73 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
74 r = util.system(cmd, environ=env, cwd=repo.root)
74 if repo:
75 cwd = repo.root
76 else:
77 cwd = os.getcwd()
78 r = util.system(cmd, environ=env, cwd=cwd)
75 if r:
79 if r:
76 desc, r = util.explain_exit(r)
80 desc, r = util.explain_exit(r)
77 if throw:
81 if throw:
78 raise util.Abort(_('%s hook %s') % (name, desc))
82 raise util.Abort(_('%s hook %s') % (name, desc))
79 ui.warn(_('warning: %s hook %s\n') % (name, desc))
83 ui.warn(_('warning: %s hook %s\n') % (name, desc))
80 return r
84 return r
81
85
82 _redirect = False
86 _redirect = False
83 def redirect(state):
87 def redirect(state):
84 _redirect = state
88 _redirect = state
85
89
86 def hook(ui, repo, name, throw=False, **args):
90 def hook(ui, repo, name, throw=False, **args):
87 r = False
91 r = False
88
92
89 if _redirect:
93 if _redirect:
90 # temporarily redirect stdout to stderr
94 # temporarily redirect stdout to stderr
91 oldstdout = os.dup(sys.stdout.fileno())
95 oldstdout = os.dup(sys.stdout.fileno())
92 os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
96 os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
93
97
94 hooks = [(hname, cmd) for hname, cmd in ui.configitems("hooks")
98 hooks = [(hname, cmd) for hname, cmd in ui.configitems("hooks")
95 if hname.split(".", 1)[0] == name and cmd]
99 if hname.split(".", 1)[0] == name and cmd]
96 hooks.sort()
100 hooks.sort()
97 for hname, cmd in hooks:
101 for hname, cmd in hooks:
98 if callable(cmd):
102 if callable(cmd):
99 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
103 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
100 elif cmd.startswith('python:'):
104 elif cmd.startswith('python:'):
101 r = _pythonhook(ui, repo, name, hname, cmd[7:].strip(),
105 r = _pythonhook(ui, repo, name, hname, cmd[7:].strip(),
102 args, throw) or r
106 args, throw) or r
103 else:
107 else:
104 r = _exthook(ui, repo, hname, cmd, args, throw) or r
108 r = _exthook(ui, repo, hname, cmd, args, throw) or r
105 return r
109 return r
106
110
107 if _redirect:
111 if _redirect:
108 os.dup2(oldstdout, sys.stdout.fileno())
112 os.dup2(oldstdout, sys.stdout.fileno())
109 os.close(oldstdout)
113 os.close(oldstdout)
General Comments 0
You need to be logged in to leave comments. Login now