##// END OF EJS Templates
dispatch: try and identify third-party extensions as sources of tracebacks...
Augie Fackler -
r16744:1c9f58a6 default
parent child Browse files
Show More
@@ -1,789 +1,827 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 ferr=None):
16 ferr=None):
17 self.args = args
17 self.args = args
18 self.ui = ui
18 self.ui = ui
19 self.repo = repo
19 self.repo = repo
20
20
21 # input/output/error streams
21 # input/output/error streams
22 self.fin = fin
22 self.fin = fin
23 self.fout = fout
23 self.fout = fout
24 self.ferr = ferr
24 self.ferr = ferr
25
25
26 def run():
26 def run():
27 "run the command in sys.argv"
27 "run the command in sys.argv"
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29
29
30 def dispatch(req):
30 def dispatch(req):
31 "run the command specified in req.args"
31 "run the command specified in req.args"
32 if req.ferr:
32 if req.ferr:
33 ferr = req.ferr
33 ferr = req.ferr
34 elif req.ui:
34 elif req.ui:
35 ferr = req.ui.ferr
35 ferr = req.ui.ferr
36 else:
36 else:
37 ferr = sys.stderr
37 ferr = sys.stderr
38
38
39 try:
39 try:
40 if not req.ui:
40 if not req.ui:
41 req.ui = uimod.ui()
41 req.ui = uimod.ui()
42 if '--traceback' in req.args:
42 if '--traceback' in req.args:
43 req.ui.setconfig('ui', 'traceback', 'on')
43 req.ui.setconfig('ui', 'traceback', 'on')
44
44
45 # set ui streams from the request
45 # set ui streams from the request
46 if req.fin:
46 if req.fin:
47 req.ui.fin = req.fin
47 req.ui.fin = req.fin
48 if req.fout:
48 if req.fout:
49 req.ui.fout = req.fout
49 req.ui.fout = req.fout
50 if req.ferr:
50 if req.ferr:
51 req.ui.ferr = req.ferr
51 req.ui.ferr = req.ferr
52 except util.Abort, inst:
52 except util.Abort, inst:
53 ferr.write(_("abort: %s\n") % inst)
53 ferr.write(_("abort: %s\n") % inst)
54 if inst.hint:
54 if inst.hint:
55 ferr.write(_("(%s)\n") % inst.hint)
55 ferr.write(_("(%s)\n") % inst.hint)
56 return -1
56 return -1
57 except error.ParseError, inst:
57 except error.ParseError, inst:
58 if len(inst.args) > 1:
58 if len(inst.args) > 1:
59 ferr.write(_("hg: parse error at %s: %s\n") %
59 ferr.write(_("hg: parse error at %s: %s\n") %
60 (inst.args[1], inst.args[0]))
60 (inst.args[1], inst.args[0]))
61 else:
61 else:
62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 return -1
63 return -1
64
64
65 return _runcatch(req)
65 return _runcatch(req)
66
66
67 def _runcatch(req):
67 def _runcatch(req):
68 def catchterm(*args):
68 def catchterm(*args):
69 raise error.SignalInterrupt
69 raise error.SignalInterrupt
70
70
71 ui = req.ui
71 ui = req.ui
72 try:
72 try:
73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
74 num = getattr(signal, name, None)
74 num = getattr(signal, name, None)
75 if num:
75 if num:
76 signal.signal(num, catchterm)
76 signal.signal(num, catchterm)
77 except ValueError:
77 except ValueError:
78 pass # happens if called in a thread
78 pass # happens if called in a thread
79
79
80 try:
80 try:
81 try:
81 try:
82 # enter the debugger before command execution
82 # enter the debugger before command execution
83 if '--debugger' in req.args:
83 if '--debugger' in req.args:
84 ui.warn(_("entering debugger - "
84 ui.warn(_("entering debugger - "
85 "type c to continue starting hg or h for help\n"))
85 "type c to continue starting hg or h for help\n"))
86 pdb.set_trace()
86 pdb.set_trace()
87 try:
87 try:
88 return _dispatch(req)
88 return _dispatch(req)
89 finally:
89 finally:
90 ui.flush()
90 ui.flush()
91 except: # re-raises
91 except: # re-raises
92 # enter the debugger when we hit an exception
92 # enter the debugger when we hit an exception
93 if '--debugger' in req.args:
93 if '--debugger' in req.args:
94 traceback.print_exc()
94 traceback.print_exc()
95 pdb.post_mortem(sys.exc_info()[2])
95 pdb.post_mortem(sys.exc_info()[2])
96 ui.traceback()
96 ui.traceback()
97 raise
97 raise
98
98
99 # Global exception handling, alphabetically
99 # Global exception handling, alphabetically
100 # Mercurial-specific first, followed by built-in and library exceptions
100 # Mercurial-specific first, followed by built-in and library exceptions
101 except error.AmbiguousCommand, inst:
101 except error.AmbiguousCommand, inst:
102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
103 (inst.args[0], " ".join(inst.args[1])))
103 (inst.args[0], " ".join(inst.args[1])))
104 except error.ParseError, inst:
104 except error.ParseError, inst:
105 if len(inst.args) > 1:
105 if len(inst.args) > 1:
106 ui.warn(_("hg: parse error at %s: %s\n") %
106 ui.warn(_("hg: parse error at %s: %s\n") %
107 (inst.args[1], inst.args[0]))
107 (inst.args[1], inst.args[0]))
108 else:
108 else:
109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
110 return -1
110 return -1
111 except error.LockHeld, inst:
111 except error.LockHeld, inst:
112 if inst.errno == errno.ETIMEDOUT:
112 if inst.errno == errno.ETIMEDOUT:
113 reason = _('timed out waiting for lock held by %s') % inst.locker
113 reason = _('timed out waiting for lock held by %s') % inst.locker
114 else:
114 else:
115 reason = _('lock held by %s') % inst.locker
115 reason = _('lock held by %s') % inst.locker
116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
117 except error.LockUnavailable, inst:
117 except error.LockUnavailable, inst:
118 ui.warn(_("abort: could not lock %s: %s\n") %
118 ui.warn(_("abort: could not lock %s: %s\n") %
119 (inst.desc or inst.filename, inst.strerror))
119 (inst.desc or inst.filename, inst.strerror))
120 except error.CommandError, inst:
120 except error.CommandError, inst:
121 if inst.args[0]:
121 if inst.args[0]:
122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
123 commands.help_(ui, inst.args[0], full=False, command=True)
123 commands.help_(ui, inst.args[0], full=False, command=True)
124 else:
124 else:
125 ui.warn(_("hg: %s\n") % inst.args[1])
125 ui.warn(_("hg: %s\n") % inst.args[1])
126 commands.help_(ui, 'shortlist')
126 commands.help_(ui, 'shortlist')
127 except error.OutOfBandError, inst:
127 except error.OutOfBandError, inst:
128 ui.warn(_("abort: remote error:\n"))
128 ui.warn(_("abort: remote error:\n"))
129 ui.warn(''.join(inst.args))
129 ui.warn(''.join(inst.args))
130 except error.RepoError, inst:
130 except error.RepoError, inst:
131 ui.warn(_("abort: %s!\n") % inst)
131 ui.warn(_("abort: %s!\n") % inst)
132 if inst.hint:
132 if inst.hint:
133 ui.warn(_("(%s)\n") % inst.hint)
133 ui.warn(_("(%s)\n") % inst.hint)
134 except error.ResponseError, inst:
134 except error.ResponseError, inst:
135 ui.warn(_("abort: %s") % inst.args[0])
135 ui.warn(_("abort: %s") % inst.args[0])
136 if not isinstance(inst.args[1], basestring):
136 if not isinstance(inst.args[1], basestring):
137 ui.warn(" %r\n" % (inst.args[1],))
137 ui.warn(" %r\n" % (inst.args[1],))
138 elif not inst.args[1]:
138 elif not inst.args[1]:
139 ui.warn(_(" empty string\n"))
139 ui.warn(_(" empty string\n"))
140 else:
140 else:
141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
142 except error.RevlogError, inst:
142 except error.RevlogError, inst:
143 ui.warn(_("abort: %s!\n") % inst)
143 ui.warn(_("abort: %s!\n") % inst)
144 except error.SignalInterrupt:
144 except error.SignalInterrupt:
145 ui.warn(_("killed!\n"))
145 ui.warn(_("killed!\n"))
146 except error.UnknownCommand, inst:
146 except error.UnknownCommand, inst:
147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
148 try:
148 try:
149 # check if the command is in a disabled extension
149 # check if the command is in a disabled extension
150 # (but don't check for extensions themselves)
150 # (but don't check for extensions themselves)
151 commands.help_(ui, inst.args[0], unknowncmd=True)
151 commands.help_(ui, inst.args[0], unknowncmd=True)
152 except error.UnknownCommand:
152 except error.UnknownCommand:
153 commands.help_(ui, 'shortlist')
153 commands.help_(ui, 'shortlist')
154 except util.Abort, inst:
154 except util.Abort, inst:
155 ui.warn(_("abort: %s\n") % inst)
155 ui.warn(_("abort: %s\n") % inst)
156 if inst.hint:
156 if inst.hint:
157 ui.warn(_("(%s)\n") % inst.hint)
157 ui.warn(_("(%s)\n") % inst.hint)
158 except ImportError, inst:
158 except ImportError, inst:
159 ui.warn(_("abort: %s!\n") % inst)
159 ui.warn(_("abort: %s!\n") % inst)
160 m = str(inst).split()[-1]
160 m = str(inst).split()[-1]
161 if m in "mpatch bdiff".split():
161 if m in "mpatch bdiff".split():
162 ui.warn(_("(did you forget to compile extensions?)\n"))
162 ui.warn(_("(did you forget to compile extensions?)\n"))
163 elif m in "zlib".split():
163 elif m in "zlib".split():
164 ui.warn(_("(is your Python install correct?)\n"))
164 ui.warn(_("(is your Python install correct?)\n"))
165 except IOError, inst:
165 except IOError, inst:
166 if util.safehasattr(inst, "code"):
166 if util.safehasattr(inst, "code"):
167 ui.warn(_("abort: %s\n") % inst)
167 ui.warn(_("abort: %s\n") % inst)
168 elif util.safehasattr(inst, "reason"):
168 elif util.safehasattr(inst, "reason"):
169 try: # usually it is in the form (errno, strerror)
169 try: # usually it is in the form (errno, strerror)
170 reason = inst.reason.args[1]
170 reason = inst.reason.args[1]
171 except (AttributeError, IndexError):
171 except (AttributeError, IndexError):
172 # it might be anything, for example a string
172 # it might be anything, for example a string
173 reason = inst.reason
173 reason = inst.reason
174 ui.warn(_("abort: error: %s\n") % reason)
174 ui.warn(_("abort: error: %s\n") % reason)
175 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
175 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
176 if ui.debugflag:
176 if ui.debugflag:
177 ui.warn(_("broken pipe\n"))
177 ui.warn(_("broken pipe\n"))
178 elif getattr(inst, "strerror", None):
178 elif getattr(inst, "strerror", None):
179 if getattr(inst, "filename", None):
179 if getattr(inst, "filename", None):
180 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
180 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
181 else:
181 else:
182 ui.warn(_("abort: %s\n") % inst.strerror)
182 ui.warn(_("abort: %s\n") % inst.strerror)
183 else:
183 else:
184 raise
184 raise
185 except OSError, inst:
185 except OSError, inst:
186 if getattr(inst, "filename", None):
186 if getattr(inst, "filename", None):
187 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
187 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
188 else:
188 else:
189 ui.warn(_("abort: %s\n") % inst.strerror)
189 ui.warn(_("abort: %s\n") % inst.strerror)
190 except KeyboardInterrupt:
190 except KeyboardInterrupt:
191 try:
191 try:
192 ui.warn(_("interrupted!\n"))
192 ui.warn(_("interrupted!\n"))
193 except IOError, inst:
193 except IOError, inst:
194 if inst.errno == errno.EPIPE:
194 if inst.errno == errno.EPIPE:
195 if ui.debugflag:
195 if ui.debugflag:
196 ui.warn(_("\nbroken pipe\n"))
196 ui.warn(_("\nbroken pipe\n"))
197 else:
197 else:
198 raise
198 raise
199 except MemoryError:
199 except MemoryError:
200 ui.warn(_("abort: out of memory\n"))
200 ui.warn(_("abort: out of memory\n"))
201 except SystemExit, inst:
201 except SystemExit, inst:
202 # Commands shouldn't sys.exit directly, but give a return code.
202 # Commands shouldn't sys.exit directly, but give a return code.
203 # Just in case catch this and and pass exit code to caller.
203 # Just in case catch this and and pass exit code to caller.
204 return inst.code
204 return inst.code
205 except socket.error, inst:
205 except socket.error, inst:
206 ui.warn(_("abort: %s\n") % inst.args[-1])
206 ui.warn(_("abort: %s\n") % inst.args[-1])
207 except: # re-raises
207 except: # re-raises
208 ui.warn(_("** unknown exception encountered,"
208 myver = util.version()
209 " please report by visiting\n"))
209 # For compatibility checking, we discard the portion of the hg
210 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
210 # version after the + on the assumption that if a "normal
211 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
211 # user" is running a build with a + in it the packager
212 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
212 # probably built from fairly close to a tag and anyone with a
213 % util.version())
213 # 'make local' copy of hg (where the version number can be out
214 ui.warn(_("** Extensions loaded: %s\n")
214 # of date) will be clueful enough to notice the implausible
215 % ", ".join([x[0] for x in extensions.extensions()]))
215 # version number and try updating.
216 compare = myver.split('+')[0]
217 ct = tuplever(compare)
218 worst = None, ct, ''
219 for name, mod in extensions.extensions():
220 testedwith = getattr(mod, 'testedwith', 'unknown')
221 report = getattr(mod, 'buglink', _('the extension author.'))
222 if testedwith == 'unknown':
223 # We found an untested extension. It's likely the culprit.
224 worst = name, testedwith, report
225 break
226 if compare not in testedwith.split() and testedwith != 'internal':
227 tested = [tuplever(v) for v in testedwith.split()]
228 nearest = max([t for t in tested if t < ct])
229 if nearest < worst[1]:
230 worst = name, nearest, report
231 if worst[0] is not None:
232 name, testedwith, report = worst
233 if not isinstance(testedwith, str):
234 testedwith = '.'.join([str(c) for c in testedwith])
235 warning = (_('** Unknown exception encountered with '
236 'possibly-broken third-party extension %s\n'
237 '** which supports versions %s of Mercurial.\n'
238 '** Please disable %s and try your action again.\n'
239 '** If that fixes the bug please report it to %s\n')
240 % (name, testedwith, name, report))
241 else:
242 warning = (_("** unknown exception encountered, "
243 "please report by visiting\n") +
244 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
245 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
246 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
247 (_("** Extensions loaded: %s\n") %
248 ", ".join([x[0] for x in extensions.extensions()])))
249 ui.warn(warning)
216 raise
250 raise
217
251
218 return -1
252 return -1
219
253
254 def tuplever(v):
255 return tuple([int(i) for i in v.split('.')])
256
257
220 def aliasargs(fn, givenargs):
258 def aliasargs(fn, givenargs):
221 args = getattr(fn, 'args', [])
259 args = getattr(fn, 'args', [])
222 if args:
260 if args:
223 cmd = ' '.join(map(util.shellquote, args))
261 cmd = ' '.join(map(util.shellquote, args))
224
262
225 nums = []
263 nums = []
226 def replacer(m):
264 def replacer(m):
227 num = int(m.group(1)) - 1
265 num = int(m.group(1)) - 1
228 nums.append(num)
266 nums.append(num)
229 if num < len(givenargs):
267 if num < len(givenargs):
230 return givenargs[num]
268 return givenargs[num]
231 raise util.Abort(_('too few arguments for command alias'))
269 raise util.Abort(_('too few arguments for command alias'))
232 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
270 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
233 givenargs = [x for i, x in enumerate(givenargs)
271 givenargs = [x for i, x in enumerate(givenargs)
234 if i not in nums]
272 if i not in nums]
235 args = shlex.split(cmd)
273 args = shlex.split(cmd)
236 return args + givenargs
274 return args + givenargs
237
275
238 class cmdalias(object):
276 class cmdalias(object):
239 def __init__(self, name, definition, cmdtable):
277 def __init__(self, name, definition, cmdtable):
240 self.name = self.cmd = name
278 self.name = self.cmd = name
241 self.cmdname = ''
279 self.cmdname = ''
242 self.definition = definition
280 self.definition = definition
243 self.args = []
281 self.args = []
244 self.opts = []
282 self.opts = []
245 self.help = ''
283 self.help = ''
246 self.norepo = True
284 self.norepo = True
247 self.optionalrepo = False
285 self.optionalrepo = False
248 self.badalias = False
286 self.badalias = False
249
287
250 try:
288 try:
251 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
289 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
252 for alias, e in cmdtable.iteritems():
290 for alias, e in cmdtable.iteritems():
253 if e is entry:
291 if e is entry:
254 self.cmd = alias
292 self.cmd = alias
255 break
293 break
256 self.shadows = True
294 self.shadows = True
257 except error.UnknownCommand:
295 except error.UnknownCommand:
258 self.shadows = False
296 self.shadows = False
259
297
260 if not self.definition:
298 if not self.definition:
261 def fn(ui, *args):
299 def fn(ui, *args):
262 ui.warn(_("no definition for alias '%s'\n") % self.name)
300 ui.warn(_("no definition for alias '%s'\n") % self.name)
263 return 1
301 return 1
264 self.fn = fn
302 self.fn = fn
265 self.badalias = True
303 self.badalias = True
266 return
304 return
267
305
268 if self.definition.startswith('!'):
306 if self.definition.startswith('!'):
269 self.shell = True
307 self.shell = True
270 def fn(ui, *args):
308 def fn(ui, *args):
271 env = {'HG_ARGS': ' '.join((self.name,) + args)}
309 env = {'HG_ARGS': ' '.join((self.name,) + args)}
272 def _checkvar(m):
310 def _checkvar(m):
273 if m.groups()[0] == '$':
311 if m.groups()[0] == '$':
274 return m.group()
312 return m.group()
275 elif int(m.groups()[0]) <= len(args):
313 elif int(m.groups()[0]) <= len(args):
276 return m.group()
314 return m.group()
277 else:
315 else:
278 ui.debug("No argument found for substitution "
316 ui.debug("No argument found for substitution "
279 "of %i variable in alias '%s' definition."
317 "of %i variable in alias '%s' definition."
280 % (int(m.groups()[0]), self.name))
318 % (int(m.groups()[0]), self.name))
281 return ''
319 return ''
282 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
320 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
283 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
321 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
284 replace['0'] = self.name
322 replace['0'] = self.name
285 replace['@'] = ' '.join(args)
323 replace['@'] = ' '.join(args)
286 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
324 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
287 return util.system(cmd, environ=env, out=ui.fout)
325 return util.system(cmd, environ=env, out=ui.fout)
288 self.fn = fn
326 self.fn = fn
289 return
327 return
290
328
291 args = shlex.split(self.definition)
329 args = shlex.split(self.definition)
292 self.cmdname = cmd = args.pop(0)
330 self.cmdname = cmd = args.pop(0)
293 args = map(util.expandpath, args)
331 args = map(util.expandpath, args)
294
332
295 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
333 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
296 if _earlygetopt([invalidarg], args):
334 if _earlygetopt([invalidarg], args):
297 def fn(ui, *args):
335 def fn(ui, *args):
298 ui.warn(_("error in definition for alias '%s': %s may only "
336 ui.warn(_("error in definition for alias '%s': %s may only "
299 "be given on the command line\n")
337 "be given on the command line\n")
300 % (self.name, invalidarg))
338 % (self.name, invalidarg))
301 return 1
339 return 1
302
340
303 self.fn = fn
341 self.fn = fn
304 self.badalias = True
342 self.badalias = True
305 return
343 return
306
344
307 try:
345 try:
308 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
346 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
309 if len(tableentry) > 2:
347 if len(tableentry) > 2:
310 self.fn, self.opts, self.help = tableentry
348 self.fn, self.opts, self.help = tableentry
311 else:
349 else:
312 self.fn, self.opts = tableentry
350 self.fn, self.opts = tableentry
313
351
314 self.args = aliasargs(self.fn, args)
352 self.args = aliasargs(self.fn, args)
315 if cmd not in commands.norepo.split(' '):
353 if cmd not in commands.norepo.split(' '):
316 self.norepo = False
354 self.norepo = False
317 if cmd in commands.optionalrepo.split(' '):
355 if cmd in commands.optionalrepo.split(' '):
318 self.optionalrepo = True
356 self.optionalrepo = True
319 if self.help.startswith("hg " + cmd):
357 if self.help.startswith("hg " + cmd):
320 # drop prefix in old-style help lines so hg shows the alias
358 # drop prefix in old-style help lines so hg shows the alias
321 self.help = self.help[4 + len(cmd):]
359 self.help = self.help[4 + len(cmd):]
322 self.__doc__ = self.fn.__doc__
360 self.__doc__ = self.fn.__doc__
323
361
324 except error.UnknownCommand:
362 except error.UnknownCommand:
325 def fn(ui, *args):
363 def fn(ui, *args):
326 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
364 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
327 % (self.name, cmd))
365 % (self.name, cmd))
328 try:
366 try:
329 # check if the command is in a disabled extension
367 # check if the command is in a disabled extension
330 commands.help_(ui, cmd, unknowncmd=True)
368 commands.help_(ui, cmd, unknowncmd=True)
331 except error.UnknownCommand:
369 except error.UnknownCommand:
332 pass
370 pass
333 return 1
371 return 1
334 self.fn = fn
372 self.fn = fn
335 self.badalias = True
373 self.badalias = True
336 except error.AmbiguousCommand:
374 except error.AmbiguousCommand:
337 def fn(ui, *args):
375 def fn(ui, *args):
338 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
376 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
339 % (self.name, cmd))
377 % (self.name, cmd))
340 return 1
378 return 1
341 self.fn = fn
379 self.fn = fn
342 self.badalias = True
380 self.badalias = True
343
381
344 def __call__(self, ui, *args, **opts):
382 def __call__(self, ui, *args, **opts):
345 if self.shadows:
383 if self.shadows:
346 ui.debug("alias '%s' shadows command '%s'\n" %
384 ui.debug("alias '%s' shadows command '%s'\n" %
347 (self.name, self.cmdname))
385 (self.name, self.cmdname))
348
386
349 if util.safehasattr(self, 'shell'):
387 if util.safehasattr(self, 'shell'):
350 return self.fn(ui, *args, **opts)
388 return self.fn(ui, *args, **opts)
351 else:
389 else:
352 try:
390 try:
353 util.checksignature(self.fn)(ui, *args, **opts)
391 util.checksignature(self.fn)(ui, *args, **opts)
354 except error.SignatureError:
392 except error.SignatureError:
355 args = ' '.join([self.cmdname] + self.args)
393 args = ' '.join([self.cmdname] + self.args)
356 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
394 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
357 raise
395 raise
358
396
359 def addaliases(ui, cmdtable):
397 def addaliases(ui, cmdtable):
360 # aliases are processed after extensions have been loaded, so they
398 # aliases are processed after extensions have been loaded, so they
361 # may use extension commands. Aliases can also use other alias definitions,
399 # may use extension commands. Aliases can also use other alias definitions,
362 # but only if they have been defined prior to the current definition.
400 # but only if they have been defined prior to the current definition.
363 for alias, definition in ui.configitems('alias'):
401 for alias, definition in ui.configitems('alias'):
364 aliasdef = cmdalias(alias, definition, cmdtable)
402 aliasdef = cmdalias(alias, definition, cmdtable)
365
403
366 try:
404 try:
367 olddef = cmdtable[aliasdef.cmd][0]
405 olddef = cmdtable[aliasdef.cmd][0]
368 if olddef.definition == aliasdef.definition:
406 if olddef.definition == aliasdef.definition:
369 continue
407 continue
370 except (KeyError, AttributeError):
408 except (KeyError, AttributeError):
371 # definition might not exist or it might not be a cmdalias
409 # definition might not exist or it might not be a cmdalias
372 pass
410 pass
373
411
374 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
412 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
375 if aliasdef.norepo:
413 if aliasdef.norepo:
376 commands.norepo += ' %s' % alias
414 commands.norepo += ' %s' % alias
377 if aliasdef.optionalrepo:
415 if aliasdef.optionalrepo:
378 commands.optionalrepo += ' %s' % alias
416 commands.optionalrepo += ' %s' % alias
379
417
380 def _parse(ui, args):
418 def _parse(ui, args):
381 options = {}
419 options = {}
382 cmdoptions = {}
420 cmdoptions = {}
383
421
384 try:
422 try:
385 args = fancyopts.fancyopts(args, commands.globalopts, options)
423 args = fancyopts.fancyopts(args, commands.globalopts, options)
386 except fancyopts.getopt.GetoptError, inst:
424 except fancyopts.getopt.GetoptError, inst:
387 raise error.CommandError(None, inst)
425 raise error.CommandError(None, inst)
388
426
389 if args:
427 if args:
390 cmd, args = args[0], args[1:]
428 cmd, args = args[0], args[1:]
391 aliases, entry = cmdutil.findcmd(cmd, commands.table,
429 aliases, entry = cmdutil.findcmd(cmd, commands.table,
392 ui.configbool("ui", "strict"))
430 ui.configbool("ui", "strict"))
393 cmd = aliases[0]
431 cmd = aliases[0]
394 args = aliasargs(entry[0], args)
432 args = aliasargs(entry[0], args)
395 defaults = ui.config("defaults", cmd)
433 defaults = ui.config("defaults", cmd)
396 if defaults:
434 if defaults:
397 args = map(util.expandpath, shlex.split(defaults)) + args
435 args = map(util.expandpath, shlex.split(defaults)) + args
398 c = list(entry[1])
436 c = list(entry[1])
399 else:
437 else:
400 cmd = None
438 cmd = None
401 c = []
439 c = []
402
440
403 # combine global options into local
441 # combine global options into local
404 for o in commands.globalopts:
442 for o in commands.globalopts:
405 c.append((o[0], o[1], options[o[1]], o[3]))
443 c.append((o[0], o[1], options[o[1]], o[3]))
406
444
407 try:
445 try:
408 args = fancyopts.fancyopts(args, c, cmdoptions, True)
446 args = fancyopts.fancyopts(args, c, cmdoptions, True)
409 except fancyopts.getopt.GetoptError, inst:
447 except fancyopts.getopt.GetoptError, inst:
410 raise error.CommandError(cmd, inst)
448 raise error.CommandError(cmd, inst)
411
449
412 # separate global options back out
450 # separate global options back out
413 for o in commands.globalopts:
451 for o in commands.globalopts:
414 n = o[1]
452 n = o[1]
415 options[n] = cmdoptions[n]
453 options[n] = cmdoptions[n]
416 del cmdoptions[n]
454 del cmdoptions[n]
417
455
418 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
456 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
419
457
420 def _parseconfig(ui, config):
458 def _parseconfig(ui, config):
421 """parse the --config options from the command line"""
459 """parse the --config options from the command line"""
422 configs = []
460 configs = []
423
461
424 for cfg in config:
462 for cfg in config:
425 try:
463 try:
426 name, value = cfg.split('=', 1)
464 name, value = cfg.split('=', 1)
427 section, name = name.split('.', 1)
465 section, name = name.split('.', 1)
428 if not section or not name:
466 if not section or not name:
429 raise IndexError
467 raise IndexError
430 ui.setconfig(section, name, value)
468 ui.setconfig(section, name, value)
431 configs.append((section, name, value))
469 configs.append((section, name, value))
432 except (IndexError, ValueError):
470 except (IndexError, ValueError):
433 raise util.Abort(_('malformed --config option: %r '
471 raise util.Abort(_('malformed --config option: %r '
434 '(use --config section.name=value)') % cfg)
472 '(use --config section.name=value)') % cfg)
435
473
436 return configs
474 return configs
437
475
438 def _earlygetopt(aliases, args):
476 def _earlygetopt(aliases, args):
439 """Return list of values for an option (or aliases).
477 """Return list of values for an option (or aliases).
440
478
441 The values are listed in the order they appear in args.
479 The values are listed in the order they appear in args.
442 The options and values are removed from args.
480 The options and values are removed from args.
443 """
481 """
444 try:
482 try:
445 argcount = args.index("--")
483 argcount = args.index("--")
446 except ValueError:
484 except ValueError:
447 argcount = len(args)
485 argcount = len(args)
448 shortopts = [opt for opt in aliases if len(opt) == 2]
486 shortopts = [opt for opt in aliases if len(opt) == 2]
449 values = []
487 values = []
450 pos = 0
488 pos = 0
451 while pos < argcount:
489 while pos < argcount:
452 if args[pos] in aliases:
490 if args[pos] in aliases:
453 if pos + 1 >= argcount:
491 if pos + 1 >= argcount:
454 # ignore and let getopt report an error if there is no value
492 # ignore and let getopt report an error if there is no value
455 break
493 break
456 del args[pos]
494 del args[pos]
457 values.append(args.pop(pos))
495 values.append(args.pop(pos))
458 argcount -= 2
496 argcount -= 2
459 elif args[pos][:2] in shortopts:
497 elif args[pos][:2] in shortopts:
460 # short option can have no following space, e.g. hg log -Rfoo
498 # short option can have no following space, e.g. hg log -Rfoo
461 values.append(args.pop(pos)[2:])
499 values.append(args.pop(pos)[2:])
462 argcount -= 1
500 argcount -= 1
463 else:
501 else:
464 pos += 1
502 pos += 1
465 return values
503 return values
466
504
467 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
505 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
468 # run pre-hook, and abort if it fails
506 # run pre-hook, and abort if it fails
469 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
507 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
470 pats=cmdpats, opts=cmdoptions)
508 pats=cmdpats, opts=cmdoptions)
471 if ret:
509 if ret:
472 return ret
510 return ret
473 ret = _runcommand(ui, options, cmd, d)
511 ret = _runcommand(ui, options, cmd, d)
474 # run post-hook, passing command result
512 # run post-hook, passing command result
475 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
513 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
476 result=ret, pats=cmdpats, opts=cmdoptions)
514 result=ret, pats=cmdpats, opts=cmdoptions)
477 return ret
515 return ret
478
516
479 def _getlocal(ui, rpath):
517 def _getlocal(ui, rpath):
480 """Return (path, local ui object) for the given target path.
518 """Return (path, local ui object) for the given target path.
481
519
482 Takes paths in [cwd]/.hg/hgrc into account."
520 Takes paths in [cwd]/.hg/hgrc into account."
483 """
521 """
484 try:
522 try:
485 wd = os.getcwd()
523 wd = os.getcwd()
486 except OSError, e:
524 except OSError, e:
487 raise util.Abort(_("error getting current working directory: %s") %
525 raise util.Abort(_("error getting current working directory: %s") %
488 e.strerror)
526 e.strerror)
489 path = cmdutil.findrepo(wd) or ""
527 path = cmdutil.findrepo(wd) or ""
490 if not path:
528 if not path:
491 lui = ui
529 lui = ui
492 else:
530 else:
493 lui = ui.copy()
531 lui = ui.copy()
494 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
532 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
495
533
496 if rpath and rpath[-1]:
534 if rpath and rpath[-1]:
497 path = lui.expandpath(rpath[-1])
535 path = lui.expandpath(rpath[-1])
498 lui = ui.copy()
536 lui = ui.copy()
499 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
537 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
500
538
501 return path, lui
539 return path, lui
502
540
503 def _checkshellalias(lui, ui, args):
541 def _checkshellalias(lui, ui, args):
504 options = {}
542 options = {}
505
543
506 try:
544 try:
507 args = fancyopts.fancyopts(args, commands.globalopts, options)
545 args = fancyopts.fancyopts(args, commands.globalopts, options)
508 except fancyopts.getopt.GetoptError:
546 except fancyopts.getopt.GetoptError:
509 return
547 return
510
548
511 if not args:
549 if not args:
512 return
550 return
513
551
514 norepo = commands.norepo
552 norepo = commands.norepo
515 optionalrepo = commands.optionalrepo
553 optionalrepo = commands.optionalrepo
516 def restorecommands():
554 def restorecommands():
517 commands.norepo = norepo
555 commands.norepo = norepo
518 commands.optionalrepo = optionalrepo
556 commands.optionalrepo = optionalrepo
519
557
520 cmdtable = commands.table.copy()
558 cmdtable = commands.table.copy()
521 addaliases(lui, cmdtable)
559 addaliases(lui, cmdtable)
522
560
523 cmd = args[0]
561 cmd = args[0]
524 try:
562 try:
525 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
563 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
526 lui.configbool("ui", "strict"))
564 lui.configbool("ui", "strict"))
527 except (error.AmbiguousCommand, error.UnknownCommand):
565 except (error.AmbiguousCommand, error.UnknownCommand):
528 restorecommands()
566 restorecommands()
529 return
567 return
530
568
531 cmd = aliases[0]
569 cmd = aliases[0]
532 fn = entry[0]
570 fn = entry[0]
533
571
534 if cmd and util.safehasattr(fn, 'shell'):
572 if cmd and util.safehasattr(fn, 'shell'):
535 d = lambda: fn(ui, *args[1:])
573 d = lambda: fn(ui, *args[1:])
536 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
574 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
537 [], {})
575 [], {})
538
576
539 restorecommands()
577 restorecommands()
540
578
541 _loaded = set()
579 _loaded = set()
542 def _dispatch(req):
580 def _dispatch(req):
543 args = req.args
581 args = req.args
544 ui = req.ui
582 ui = req.ui
545
583
546 # read --config before doing anything else
584 # read --config before doing anything else
547 # (e.g. to change trust settings for reading .hg/hgrc)
585 # (e.g. to change trust settings for reading .hg/hgrc)
548 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
586 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
549
587
550 # check for cwd
588 # check for cwd
551 cwd = _earlygetopt(['--cwd'], args)
589 cwd = _earlygetopt(['--cwd'], args)
552 if cwd:
590 if cwd:
553 os.chdir(cwd[-1])
591 os.chdir(cwd[-1])
554
592
555 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
593 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
556 path, lui = _getlocal(ui, rpath)
594 path, lui = _getlocal(ui, rpath)
557
595
558 # Now that we're operating in the right directory/repository with
596 # Now that we're operating in the right directory/repository with
559 # the right config settings, check for shell aliases
597 # the right config settings, check for shell aliases
560 shellaliasfn = _checkshellalias(lui, ui, args)
598 shellaliasfn = _checkshellalias(lui, ui, args)
561 if shellaliasfn:
599 if shellaliasfn:
562 return shellaliasfn()
600 return shellaliasfn()
563
601
564 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
602 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
565 # reposetup. Programs like TortoiseHg will call _dispatch several
603 # reposetup. Programs like TortoiseHg will call _dispatch several
566 # times so we keep track of configured extensions in _loaded.
604 # times so we keep track of configured extensions in _loaded.
567 extensions.loadall(lui)
605 extensions.loadall(lui)
568 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
606 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
569 # Propagate any changes to lui.__class__ by extensions
607 # Propagate any changes to lui.__class__ by extensions
570 ui.__class__ = lui.__class__
608 ui.__class__ = lui.__class__
571
609
572 # (uisetup and extsetup are handled in extensions.loadall)
610 # (uisetup and extsetup are handled in extensions.loadall)
573
611
574 for name, module in exts:
612 for name, module in exts:
575 cmdtable = getattr(module, 'cmdtable', {})
613 cmdtable = getattr(module, 'cmdtable', {})
576 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
614 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
577 if overrides:
615 if overrides:
578 ui.warn(_("extension '%s' overrides commands: %s\n")
616 ui.warn(_("extension '%s' overrides commands: %s\n")
579 % (name, " ".join(overrides)))
617 % (name, " ".join(overrides)))
580 commands.table.update(cmdtable)
618 commands.table.update(cmdtable)
581 _loaded.add(name)
619 _loaded.add(name)
582
620
583 # (reposetup is handled in hg.repository)
621 # (reposetup is handled in hg.repository)
584
622
585 addaliases(lui, commands.table)
623 addaliases(lui, commands.table)
586
624
587 # check for fallback encoding
625 # check for fallback encoding
588 fallback = lui.config('ui', 'fallbackencoding')
626 fallback = lui.config('ui', 'fallbackencoding')
589 if fallback:
627 if fallback:
590 encoding.fallbackencoding = fallback
628 encoding.fallbackencoding = fallback
591
629
592 fullargs = args
630 fullargs = args
593 cmd, func, args, options, cmdoptions = _parse(lui, args)
631 cmd, func, args, options, cmdoptions = _parse(lui, args)
594
632
595 if options["config"]:
633 if options["config"]:
596 raise util.Abort(_("option --config may not be abbreviated!"))
634 raise util.Abort(_("option --config may not be abbreviated!"))
597 if options["cwd"]:
635 if options["cwd"]:
598 raise util.Abort(_("option --cwd may not be abbreviated!"))
636 raise util.Abort(_("option --cwd may not be abbreviated!"))
599 if options["repository"]:
637 if options["repository"]:
600 raise util.Abort(_(
638 raise util.Abort(_(
601 "option -R has to be separated from other options (e.g. not -qR) "
639 "option -R has to be separated from other options (e.g. not -qR) "
602 "and --repository may only be abbreviated as --repo!"))
640 "and --repository may only be abbreviated as --repo!"))
603
641
604 if options["encoding"]:
642 if options["encoding"]:
605 encoding.encoding = options["encoding"]
643 encoding.encoding = options["encoding"]
606 if options["encodingmode"]:
644 if options["encodingmode"]:
607 encoding.encodingmode = options["encodingmode"]
645 encoding.encodingmode = options["encodingmode"]
608 if options["time"]:
646 if options["time"]:
609 def get_times():
647 def get_times():
610 t = os.times()
648 t = os.times()
611 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
649 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
612 t = (t[0], t[1], t[2], t[3], time.clock())
650 t = (t[0], t[1], t[2], t[3], time.clock())
613 return t
651 return t
614 s = get_times()
652 s = get_times()
615 def print_time():
653 def print_time():
616 t = get_times()
654 t = get_times()
617 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
655 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
618 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
656 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
619 atexit.register(print_time)
657 atexit.register(print_time)
620
658
621 uis = set([ui, lui])
659 uis = set([ui, lui])
622
660
623 if req.repo:
661 if req.repo:
624 uis.add(req.repo.ui)
662 uis.add(req.repo.ui)
625
663
626 # copy configs that were passed on the cmdline (--config) to the repo ui
664 # copy configs that were passed on the cmdline (--config) to the repo ui
627 for cfg in cfgs:
665 for cfg in cfgs:
628 req.repo.ui.setconfig(*cfg)
666 req.repo.ui.setconfig(*cfg)
629
667
630 if options['verbose'] or options['debug'] or options['quiet']:
668 if options['verbose'] or options['debug'] or options['quiet']:
631 for opt in ('verbose', 'debug', 'quiet'):
669 for opt in ('verbose', 'debug', 'quiet'):
632 val = str(bool(options[opt]))
670 val = str(bool(options[opt]))
633 for ui_ in uis:
671 for ui_ in uis:
634 ui_.setconfig('ui', opt, val)
672 ui_.setconfig('ui', opt, val)
635
673
636 if options['traceback']:
674 if options['traceback']:
637 for ui_ in uis:
675 for ui_ in uis:
638 ui_.setconfig('ui', 'traceback', 'on')
676 ui_.setconfig('ui', 'traceback', 'on')
639
677
640 if options['noninteractive']:
678 if options['noninteractive']:
641 for ui_ in uis:
679 for ui_ in uis:
642 ui_.setconfig('ui', 'interactive', 'off')
680 ui_.setconfig('ui', 'interactive', 'off')
643
681
644 if cmdoptions.get('insecure', False):
682 if cmdoptions.get('insecure', False):
645 for ui_ in uis:
683 for ui_ in uis:
646 ui_.setconfig('web', 'cacerts', '')
684 ui_.setconfig('web', 'cacerts', '')
647
685
648 if options['version']:
686 if options['version']:
649 return commands.version_(ui)
687 return commands.version_(ui)
650 if options['help']:
688 if options['help']:
651 return commands.help_(ui, cmd)
689 return commands.help_(ui, cmd)
652 elif not cmd:
690 elif not cmd:
653 return commands.help_(ui, 'shortlist')
691 return commands.help_(ui, 'shortlist')
654
692
655 repo = None
693 repo = None
656 cmdpats = args[:]
694 cmdpats = args[:]
657 if cmd not in commands.norepo.split():
695 if cmd not in commands.norepo.split():
658 # use the repo from the request only if we don't have -R
696 # use the repo from the request only if we don't have -R
659 if not rpath and not cwd:
697 if not rpath and not cwd:
660 repo = req.repo
698 repo = req.repo
661
699
662 if repo:
700 if repo:
663 # set the descriptors of the repo ui to those of ui
701 # set the descriptors of the repo ui to those of ui
664 repo.ui.fin = ui.fin
702 repo.ui.fin = ui.fin
665 repo.ui.fout = ui.fout
703 repo.ui.fout = ui.fout
666 repo.ui.ferr = ui.ferr
704 repo.ui.ferr = ui.ferr
667 else:
705 else:
668 try:
706 try:
669 repo = hg.repository(ui, path=path)
707 repo = hg.repository(ui, path=path)
670 if not repo.local():
708 if not repo.local():
671 raise util.Abort(_("repository '%s' is not local") % path)
709 raise util.Abort(_("repository '%s' is not local") % path)
672 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
710 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
673 except error.RequirementError:
711 except error.RequirementError:
674 raise
712 raise
675 except error.RepoError:
713 except error.RepoError:
676 if cmd not in commands.optionalrepo.split():
714 if cmd not in commands.optionalrepo.split():
677 if args and not path: # try to infer -R from command args
715 if args and not path: # try to infer -R from command args
678 repos = map(cmdutil.findrepo, args)
716 repos = map(cmdutil.findrepo, args)
679 guess = repos[0]
717 guess = repos[0]
680 if guess and repos.count(guess) == len(repos):
718 if guess and repos.count(guess) == len(repos):
681 req.args = ['--repository', guess] + fullargs
719 req.args = ['--repository', guess] + fullargs
682 return _dispatch(req)
720 return _dispatch(req)
683 if not path:
721 if not path:
684 raise error.RepoError(_("no repository found in '%s'"
722 raise error.RepoError(_("no repository found in '%s'"
685 " (.hg not found)")
723 " (.hg not found)")
686 % os.getcwd())
724 % os.getcwd())
687 raise
725 raise
688 if repo:
726 if repo:
689 ui = repo.ui
727 ui = repo.ui
690 args.insert(0, repo)
728 args.insert(0, repo)
691 elif rpath:
729 elif rpath:
692 ui.warn(_("warning: --repository ignored\n"))
730 ui.warn(_("warning: --repository ignored\n"))
693
731
694 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
732 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
695 ui.log("command", msg + "\n")
733 ui.log("command", msg + "\n")
696 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
734 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
697 try:
735 try:
698 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
736 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
699 cmdpats, cmdoptions)
737 cmdpats, cmdoptions)
700 finally:
738 finally:
701 if repo and repo != req.repo:
739 if repo and repo != req.repo:
702 repo.close()
740 repo.close()
703
741
704 def lsprofile(ui, func, fp):
742 def lsprofile(ui, func, fp):
705 format = ui.config('profiling', 'format', default='text')
743 format = ui.config('profiling', 'format', default='text')
706 field = ui.config('profiling', 'sort', default='inlinetime')
744 field = ui.config('profiling', 'sort', default='inlinetime')
707 climit = ui.configint('profiling', 'nested', default=5)
745 climit = ui.configint('profiling', 'nested', default=5)
708
746
709 if format not in ['text', 'kcachegrind']:
747 if format not in ['text', 'kcachegrind']:
710 ui.warn(_("unrecognized profiling format '%s'"
748 ui.warn(_("unrecognized profiling format '%s'"
711 " - Ignored\n") % format)
749 " - Ignored\n") % format)
712 format = 'text'
750 format = 'text'
713
751
714 try:
752 try:
715 from mercurial import lsprof
753 from mercurial import lsprof
716 except ImportError:
754 except ImportError:
717 raise util.Abort(_(
755 raise util.Abort(_(
718 'lsprof not available - install from '
756 'lsprof not available - install from '
719 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
757 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
720 p = lsprof.Profiler()
758 p = lsprof.Profiler()
721 p.enable(subcalls=True)
759 p.enable(subcalls=True)
722 try:
760 try:
723 return func()
761 return func()
724 finally:
762 finally:
725 p.disable()
763 p.disable()
726
764
727 if format == 'kcachegrind':
765 if format == 'kcachegrind':
728 import lsprofcalltree
766 import lsprofcalltree
729 calltree = lsprofcalltree.KCacheGrind(p)
767 calltree = lsprofcalltree.KCacheGrind(p)
730 calltree.output(fp)
768 calltree.output(fp)
731 else:
769 else:
732 # format == 'text'
770 # format == 'text'
733 stats = lsprof.Stats(p.getstats())
771 stats = lsprof.Stats(p.getstats())
734 stats.sort(field)
772 stats.sort(field)
735 stats.pprint(limit=30, file=fp, climit=climit)
773 stats.pprint(limit=30, file=fp, climit=climit)
736
774
737 def statprofile(ui, func, fp):
775 def statprofile(ui, func, fp):
738 try:
776 try:
739 import statprof
777 import statprof
740 except ImportError:
778 except ImportError:
741 raise util.Abort(_(
779 raise util.Abort(_(
742 'statprof not available - install using "easy_install statprof"'))
780 'statprof not available - install using "easy_install statprof"'))
743
781
744 freq = ui.configint('profiling', 'freq', default=1000)
782 freq = ui.configint('profiling', 'freq', default=1000)
745 if freq > 0:
783 if freq > 0:
746 statprof.reset(freq)
784 statprof.reset(freq)
747 else:
785 else:
748 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
786 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
749
787
750 statprof.start()
788 statprof.start()
751 try:
789 try:
752 return func()
790 return func()
753 finally:
791 finally:
754 statprof.stop()
792 statprof.stop()
755 statprof.display(fp)
793 statprof.display(fp)
756
794
757 def _runcommand(ui, options, cmd, cmdfunc):
795 def _runcommand(ui, options, cmd, cmdfunc):
758 def checkargs():
796 def checkargs():
759 try:
797 try:
760 return cmdfunc()
798 return cmdfunc()
761 except error.SignatureError:
799 except error.SignatureError:
762 raise error.CommandError(cmd, _("invalid arguments"))
800 raise error.CommandError(cmd, _("invalid arguments"))
763
801
764 if options['profile']:
802 if options['profile']:
765 profiler = os.getenv('HGPROF')
803 profiler = os.getenv('HGPROF')
766 if profiler is None:
804 if profiler is None:
767 profiler = ui.config('profiling', 'type', default='ls')
805 profiler = ui.config('profiling', 'type', default='ls')
768 if profiler not in ('ls', 'stat'):
806 if profiler not in ('ls', 'stat'):
769 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
807 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
770 profiler = 'ls'
808 profiler = 'ls'
771
809
772 output = ui.config('profiling', 'output')
810 output = ui.config('profiling', 'output')
773
811
774 if output:
812 if output:
775 path = ui.expandpath(output)
813 path = ui.expandpath(output)
776 fp = open(path, 'wb')
814 fp = open(path, 'wb')
777 else:
815 else:
778 fp = sys.stderr
816 fp = sys.stderr
779
817
780 try:
818 try:
781 if profiler == 'ls':
819 if profiler == 'ls':
782 return lsprofile(ui, checkargs, fp)
820 return lsprofile(ui, checkargs, fp)
783 else:
821 else:
784 return statprofile(ui, checkargs, fp)
822 return statprofile(ui, checkargs, fp)
785 finally:
823 finally:
786 if output:
824 if output:
787 fp.close()
825 fp.close()
788 else:
826 else:
789 return checkargs()
827 return checkargs()
@@ -1,480 +1,537 b''
1 Test basic extension support
1 Test basic extension support
2
2
3 $ "$TESTDIR/hghave" no-outer-repo || exit 80
3 $ "$TESTDIR/hghave" no-outer-repo || exit 80
4
4
5 $ cat > foobar.py <<EOF
5 $ cat > foobar.py <<EOF
6 > import os
6 > import os
7 > from mercurial import commands
7 > from mercurial import commands
8 >
8 >
9 > def uisetup(ui):
9 > def uisetup(ui):
10 > ui.write("uisetup called\\n")
10 > ui.write("uisetup called\\n")
11 >
11 >
12 > def reposetup(ui, repo):
12 > def reposetup(ui, repo):
13 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
13 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
14 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
14 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
15 >
15 >
16 > def foo(ui, *args, **kwargs):
16 > def foo(ui, *args, **kwargs):
17 > ui.write("Foo\\n")
17 > ui.write("Foo\\n")
18 >
18 >
19 > def bar(ui, *args, **kwargs):
19 > def bar(ui, *args, **kwargs):
20 > ui.write("Bar\\n")
20 > ui.write("Bar\\n")
21 >
21 >
22 > cmdtable = {
22 > cmdtable = {
23 > "foo": (foo, [], "hg foo"),
23 > "foo": (foo, [], "hg foo"),
24 > "bar": (bar, [], "hg bar"),
24 > "bar": (bar, [], "hg bar"),
25 > }
25 > }
26 >
26 >
27 > commands.norepo += ' bar'
27 > commands.norepo += ' bar'
28 > EOF
28 > EOF
29 $ abspath=`pwd`/foobar.py
29 $ abspath=`pwd`/foobar.py
30
30
31 $ mkdir barfoo
31 $ mkdir barfoo
32 $ cp foobar.py barfoo/__init__.py
32 $ cp foobar.py barfoo/__init__.py
33 $ barfoopath=`pwd`/barfoo
33 $ barfoopath=`pwd`/barfoo
34
34
35 $ hg init a
35 $ hg init a
36 $ cd a
36 $ cd a
37 $ echo foo > file
37 $ echo foo > file
38 $ hg add file
38 $ hg add file
39 $ hg commit -m 'add file'
39 $ hg commit -m 'add file'
40
40
41 $ echo '[extensions]' >> $HGRCPATH
41 $ echo '[extensions]' >> $HGRCPATH
42 $ echo "foobar = $abspath" >> $HGRCPATH
42 $ echo "foobar = $abspath" >> $HGRCPATH
43 $ hg foo
43 $ hg foo
44 uisetup called
44 uisetup called
45 reposetup called for a
45 reposetup called for a
46 ui == repo.ui
46 ui == repo.ui
47 Foo
47 Foo
48
48
49 $ cd ..
49 $ cd ..
50 $ hg clone a b
50 $ hg clone a b
51 uisetup called
51 uisetup called
52 reposetup called for a
52 reposetup called for a
53 ui == repo.ui
53 ui == repo.ui
54 reposetup called for b
54 reposetup called for b
55 ui == repo.ui
55 ui == repo.ui
56 updating to branch default
56 updating to branch default
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58
58
59 $ hg bar
59 $ hg bar
60 uisetup called
60 uisetup called
61 Bar
61 Bar
62 $ echo 'foobar = !' >> $HGRCPATH
62 $ echo 'foobar = !' >> $HGRCPATH
63
63
64 module/__init__.py-style
64 module/__init__.py-style
65
65
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
66 $ echo "barfoo = $barfoopath" >> $HGRCPATH
67 $ cd a
67 $ cd a
68 $ hg foo
68 $ hg foo
69 uisetup called
69 uisetup called
70 reposetup called for a
70 reposetup called for a
71 ui == repo.ui
71 ui == repo.ui
72 Foo
72 Foo
73 $ echo 'barfoo = !' >> $HGRCPATH
73 $ echo 'barfoo = !' >> $HGRCPATH
74
74
75 Check that extensions are loaded in phases:
75 Check that extensions are loaded in phases:
76
76
77 $ cat > foo.py <<EOF
77 $ cat > foo.py <<EOF
78 > import os
78 > import os
79 > name = os.path.basename(__file__).rsplit('.', 1)[0]
79 > name = os.path.basename(__file__).rsplit('.', 1)[0]
80 > print "1) %s imported" % name
80 > print "1) %s imported" % name
81 > def uisetup(ui):
81 > def uisetup(ui):
82 > print "2) %s uisetup" % name
82 > print "2) %s uisetup" % name
83 > def extsetup():
83 > def extsetup():
84 > print "3) %s extsetup" % name
84 > print "3) %s extsetup" % name
85 > def reposetup(ui, repo):
85 > def reposetup(ui, repo):
86 > print "4) %s reposetup" % name
86 > print "4) %s reposetup" % name
87 > EOF
87 > EOF
88
88
89 $ cp foo.py bar.py
89 $ cp foo.py bar.py
90 $ echo 'foo = foo.py' >> $HGRCPATH
90 $ echo 'foo = foo.py' >> $HGRCPATH
91 $ echo 'bar = bar.py' >> $HGRCPATH
91 $ echo 'bar = bar.py' >> $HGRCPATH
92
92
93 Command with no output, we just want to see the extensions loaded:
93 Command with no output, we just want to see the extensions loaded:
94
94
95 $ hg paths
95 $ hg paths
96 1) foo imported
96 1) foo imported
97 1) bar imported
97 1) bar imported
98 2) foo uisetup
98 2) foo uisetup
99 2) bar uisetup
99 2) bar uisetup
100 3) foo extsetup
100 3) foo extsetup
101 3) bar extsetup
101 3) bar extsetup
102 4) foo reposetup
102 4) foo reposetup
103 4) bar reposetup
103 4) bar reposetup
104
104
105 Check hgweb's load order:
105 Check hgweb's load order:
106
106
107 $ cat > hgweb.cgi <<EOF
107 $ cat > hgweb.cgi <<EOF
108 > #!/usr/bin/env python
108 > #!/usr/bin/env python
109 > from mercurial import demandimport; demandimport.enable()
109 > from mercurial import demandimport; demandimport.enable()
110 > from mercurial.hgweb import hgweb
110 > from mercurial.hgweb import hgweb
111 > from mercurial.hgweb import wsgicgi
111 > from mercurial.hgweb import wsgicgi
112 >
112 >
113 > application = hgweb('.', 'test repo')
113 > application = hgweb('.', 'test repo')
114 > wsgicgi.launch(application)
114 > wsgicgi.launch(application)
115 > EOF
115 > EOF
116
116
117 $ SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
117 $ SCRIPT_NAME='/' SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
118 > | grep '^[0-9]) ' # ignores HTML output
118 > | grep '^[0-9]) ' # ignores HTML output
119 1) foo imported
119 1) foo imported
120 1) bar imported
120 1) bar imported
121 2) foo uisetup
121 2) foo uisetup
122 2) bar uisetup
122 2) bar uisetup
123 3) foo extsetup
123 3) foo extsetup
124 3) bar extsetup
124 3) bar extsetup
125 4) foo reposetup
125 4) foo reposetup
126 4) bar reposetup
126 4) bar reposetup
127 4) foo reposetup
127 4) foo reposetup
128 4) bar reposetup
128 4) bar reposetup
129
129
130 $ echo 'foo = !' >> $HGRCPATH
130 $ echo 'foo = !' >> $HGRCPATH
131 $ echo 'bar = !' >> $HGRCPATH
131 $ echo 'bar = !' >> $HGRCPATH
132
132
133 $ cd ..
133 $ cd ..
134
134
135 $ cat > empty.py <<EOF
135 $ cat > empty.py <<EOF
136 > '''empty cmdtable
136 > '''empty cmdtable
137 > '''
137 > '''
138 > cmdtable = {}
138 > cmdtable = {}
139 > EOF
139 > EOF
140 $ emptypath=`pwd`/empty.py
140 $ emptypath=`pwd`/empty.py
141 $ echo "empty = $emptypath" >> $HGRCPATH
141 $ echo "empty = $emptypath" >> $HGRCPATH
142 $ hg help empty
142 $ hg help empty
143 empty extension - empty cmdtable
143 empty extension - empty cmdtable
144
144
145 no commands defined
145 no commands defined
146
146
147 $ echo 'empty = !' >> $HGRCPATH
147 $ echo 'empty = !' >> $HGRCPATH
148
148
149 $ cat > debugextension.py <<EOF
149 $ cat > debugextension.py <<EOF
150 > '''only debugcommands
150 > '''only debugcommands
151 > '''
151 > '''
152 > def debugfoobar(ui, repo, *args, **opts):
152 > def debugfoobar(ui, repo, *args, **opts):
153 > "yet another debug command"
153 > "yet another debug command"
154 > pass
154 > pass
155 >
155 >
156 > def foo(ui, repo, *args, **opts):
156 > def foo(ui, repo, *args, **opts):
157 > """yet another foo command
157 > """yet another foo command
158 >
158 >
159 > This command has been DEPRECATED since forever.
159 > This command has been DEPRECATED since forever.
160 > """
160 > """
161 > pass
161 > pass
162 >
162 >
163 > cmdtable = {
163 > cmdtable = {
164 > "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
164 > "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
165 > "foo": (foo, (), "hg foo")
165 > "foo": (foo, (), "hg foo")
166 > }
166 > }
167 > EOF
167 > EOF
168 $ debugpath=`pwd`/debugextension.py
168 $ debugpath=`pwd`/debugextension.py
169 $ echo "debugextension = $debugpath" >> $HGRCPATH
169 $ echo "debugextension = $debugpath" >> $HGRCPATH
170
170
171 $ hg help debugextension
171 $ hg help debugextension
172 debugextension extension - only debugcommands
172 debugextension extension - only debugcommands
173
173
174 no commands defined
174 no commands defined
175
175
176 $ hg --verbose help debugextension
176 $ hg --verbose help debugextension
177 debugextension extension - only debugcommands
177 debugextension extension - only debugcommands
178
178
179 list of commands:
179 list of commands:
180
180
181 foo:
181 foo:
182 yet another foo command
182 yet another foo command
183
183
184 global options:
184 global options:
185
185
186 -R --repository REPO repository root directory or name of overlay bundle
186 -R --repository REPO repository root directory or name of overlay bundle
187 file
187 file
188 --cwd DIR change working directory
188 --cwd DIR change working directory
189 -y --noninteractive do not prompt, automatically pick the first choice for
189 -y --noninteractive do not prompt, automatically pick the first choice for
190 all prompts
190 all prompts
191 -q --quiet suppress output
191 -q --quiet suppress output
192 -v --verbose enable additional output
192 -v --verbose enable additional output
193 --config CONFIG [+] set/override config option (use 'section.name=value')
193 --config CONFIG [+] set/override config option (use 'section.name=value')
194 --debug enable debugging output
194 --debug enable debugging output
195 --debugger start debugger
195 --debugger start debugger
196 --encoding ENCODE set the charset encoding (default: ascii)
196 --encoding ENCODE set the charset encoding (default: ascii)
197 --encodingmode MODE set the charset encoding mode (default: strict)
197 --encodingmode MODE set the charset encoding mode (default: strict)
198 --traceback always print a traceback on exception
198 --traceback always print a traceback on exception
199 --time time how long the command takes
199 --time time how long the command takes
200 --profile print command execution profile
200 --profile print command execution profile
201 --version output version information and exit
201 --version output version information and exit
202 -h --help display help and exit
202 -h --help display help and exit
203
203
204 [+] marked option can be specified multiple times
204 [+] marked option can be specified multiple times
205
205
206 $ hg --debug help debugextension
206 $ hg --debug help debugextension
207 debugextension extension - only debugcommands
207 debugextension extension - only debugcommands
208
208
209 list of commands:
209 list of commands:
210
210
211 debugfoobar:
211 debugfoobar:
212 yet another debug command
212 yet another debug command
213 foo:
213 foo:
214 yet another foo command
214 yet another foo command
215
215
216 global options:
216 global options:
217
217
218 -R --repository REPO repository root directory or name of overlay bundle
218 -R --repository REPO repository root directory or name of overlay bundle
219 file
219 file
220 --cwd DIR change working directory
220 --cwd DIR change working directory
221 -y --noninteractive do not prompt, automatically pick the first choice for
221 -y --noninteractive do not prompt, automatically pick the first choice for
222 all prompts
222 all prompts
223 -q --quiet suppress output
223 -q --quiet suppress output
224 -v --verbose enable additional output
224 -v --verbose enable additional output
225 --config CONFIG [+] set/override config option (use 'section.name=value')
225 --config CONFIG [+] set/override config option (use 'section.name=value')
226 --debug enable debugging output
226 --debug enable debugging output
227 --debugger start debugger
227 --debugger start debugger
228 --encoding ENCODE set the charset encoding (default: ascii)
228 --encoding ENCODE set the charset encoding (default: ascii)
229 --encodingmode MODE set the charset encoding mode (default: strict)
229 --encodingmode MODE set the charset encoding mode (default: strict)
230 --traceback always print a traceback on exception
230 --traceback always print a traceback on exception
231 --time time how long the command takes
231 --time time how long the command takes
232 --profile print command execution profile
232 --profile print command execution profile
233 --version output version information and exit
233 --version output version information and exit
234 -h --help display help and exit
234 -h --help display help and exit
235
235
236 [+] marked option can be specified multiple times
236 [+] marked option can be specified multiple times
237 $ echo 'debugextension = !' >> $HGRCPATH
237 $ echo 'debugextension = !' >> $HGRCPATH
238
238
239 Extension module help vs command help:
239 Extension module help vs command help:
240
240
241 $ echo 'extdiff =' >> $HGRCPATH
241 $ echo 'extdiff =' >> $HGRCPATH
242 $ hg help extdiff
242 $ hg help extdiff
243 hg extdiff [OPT]... [FILE]...
243 hg extdiff [OPT]... [FILE]...
244
244
245 use external program to diff repository (or selected files)
245 use external program to diff repository (or selected files)
246
246
247 Show differences between revisions for the specified files, using an
247 Show differences between revisions for the specified files, using an
248 external program. The default program used is diff, with default options
248 external program. The default program used is diff, with default options
249 "-Npru".
249 "-Npru".
250
250
251 To select a different program, use the -p/--program option. The program
251 To select a different program, use the -p/--program option. The program
252 will be passed the names of two directories to compare. To pass additional
252 will be passed the names of two directories to compare. To pass additional
253 options to the program, use -o/--option. These will be passed before the
253 options to the program, use -o/--option. These will be passed before the
254 names of the directories to compare.
254 names of the directories to compare.
255
255
256 When two revision arguments are given, then changes are shown between
256 When two revision arguments are given, then changes are shown between
257 those revisions. If only one revision is specified then that revision is
257 those revisions. If only one revision is specified then that revision is
258 compared to the working directory, and, when no revisions are specified,
258 compared to the working directory, and, when no revisions are specified,
259 the working directory files are compared to its parent.
259 the working directory files are compared to its parent.
260
260
261 use "hg help -e extdiff" to show help for the extdiff extension
261 use "hg help -e extdiff" to show help for the extdiff extension
262
262
263 options:
263 options:
264
264
265 -p --program CMD comparison program to run
265 -p --program CMD comparison program to run
266 -o --option OPT [+] pass option to comparison program
266 -o --option OPT [+] pass option to comparison program
267 -r --rev REV [+] revision
267 -r --rev REV [+] revision
268 -c --change REV change made by revision
268 -c --change REV change made by revision
269 -I --include PATTERN [+] include names matching the given patterns
269 -I --include PATTERN [+] include names matching the given patterns
270 -X --exclude PATTERN [+] exclude names matching the given patterns
270 -X --exclude PATTERN [+] exclude names matching the given patterns
271
271
272 [+] marked option can be specified multiple times
272 [+] marked option can be specified multiple times
273
273
274 use "hg -v help extdiff" to show more info
274 use "hg -v help extdiff" to show more info
275
275
276 $ hg help --extension extdiff
276 $ hg help --extension extdiff
277 extdiff extension - command to allow external programs to compare revisions
277 extdiff extension - command to allow external programs to compare revisions
278
278
279 The extdiff Mercurial extension allows you to use external programs to compare
279 The extdiff Mercurial extension allows you to use external programs to compare
280 revisions, or revision with working directory. The external diff programs are
280 revisions, or revision with working directory. The external diff programs are
281 called with a configurable set of options and two non-option arguments: paths
281 called with a configurable set of options and two non-option arguments: paths
282 to directories containing snapshots of files to compare.
282 to directories containing snapshots of files to compare.
283
283
284 The extdiff extension also allows you to configure new diff commands, so you
284 The extdiff extension also allows you to configure new diff commands, so you
285 do not need to type "hg extdiff -p kdiff3" always.
285 do not need to type "hg extdiff -p kdiff3" always.
286
286
287 [extdiff]
287 [extdiff]
288 # add new command that runs GNU diff(1) in 'context diff' mode
288 # add new command that runs GNU diff(1) in 'context diff' mode
289 cdiff = gdiff -Nprc5
289 cdiff = gdiff -Nprc5
290 ## or the old way:
290 ## or the old way:
291 #cmd.cdiff = gdiff
291 #cmd.cdiff = gdiff
292 #opts.cdiff = -Nprc5
292 #opts.cdiff = -Nprc5
293
293
294 # add new command called vdiff, runs kdiff3
294 # add new command called vdiff, runs kdiff3
295 vdiff = kdiff3
295 vdiff = kdiff3
296
296
297 # add new command called meld, runs meld (no need to name twice)
297 # add new command called meld, runs meld (no need to name twice)
298 meld =
298 meld =
299
299
300 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
300 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
301 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
301 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
302 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
302 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
303 # your .vimrc
303 # your .vimrc
304 vimdiff = gvim -f "+next" \
304 vimdiff = gvim -f "+next" \
305 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
305 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
306
306
307 Tool arguments can include variables that are expanded at runtime:
307 Tool arguments can include variables that are expanded at runtime:
308
308
309 $parent1, $plabel1 - filename, descriptive label of first parent
309 $parent1, $plabel1 - filename, descriptive label of first parent
310 $child, $clabel - filename, descriptive label of child revision
310 $child, $clabel - filename, descriptive label of child revision
311 $parent2, $plabel2 - filename, descriptive label of second parent
311 $parent2, $plabel2 - filename, descriptive label of second parent
312 $root - repository root
312 $root - repository root
313 $parent is an alias for $parent1.
313 $parent is an alias for $parent1.
314
314
315 The extdiff extension will look in your [diff-tools] and [merge-tools]
315 The extdiff extension will look in your [diff-tools] and [merge-tools]
316 sections for diff tool arguments, when none are specified in [extdiff].
316 sections for diff tool arguments, when none are specified in [extdiff].
317
317
318 [extdiff]
318 [extdiff]
319 kdiff3 =
319 kdiff3 =
320
320
321 [diff-tools]
321 [diff-tools]
322 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
322 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
323
323
324 You can use -I/-X and list of file or directory names like normal "hg diff"
324 You can use -I/-X and list of file or directory names like normal "hg diff"
325 command. The extdiff extension makes snapshots of only needed files, so
325 command. The extdiff extension makes snapshots of only needed files, so
326 running the external diff program will actually be pretty fast (at least
326 running the external diff program will actually be pretty fast (at least
327 faster than having to compare the entire tree).
327 faster than having to compare the entire tree).
328
328
329 list of commands:
329 list of commands:
330
330
331 extdiff use external program to diff repository (or selected files)
331 extdiff use external program to diff repository (or selected files)
332
332
333 use "hg -v help extdiff" to show builtin aliases and global options
333 use "hg -v help extdiff" to show builtin aliases and global options
334
334
335 $ echo 'extdiff = !' >> $HGRCPATH
335 $ echo 'extdiff = !' >> $HGRCPATH
336
336
337 Test help topic with same name as extension
337 Test help topic with same name as extension
338
338
339 $ cat > multirevs.py <<EOF
339 $ cat > multirevs.py <<EOF
340 > from mercurial import commands
340 > from mercurial import commands
341 > """multirevs extension
341 > """multirevs extension
342 > Big multi-line module docstring."""
342 > Big multi-line module docstring."""
343 > def multirevs(ui, repo, arg, *args, **opts):
343 > def multirevs(ui, repo, arg, *args, **opts):
344 > """multirevs command"""
344 > """multirevs command"""
345 > pass
345 > pass
346 > cmdtable = {
346 > cmdtable = {
347 > "multirevs": (multirevs, [], 'ARG')
347 > "multirevs": (multirevs, [], 'ARG')
348 > }
348 > }
349 > commands.norepo += ' multirevs'
349 > commands.norepo += ' multirevs'
350 > EOF
350 > EOF
351 $ echo "multirevs = multirevs.py" >> $HGRCPATH
351 $ echo "multirevs = multirevs.py" >> $HGRCPATH
352
352
353 $ hg help multirevs
353 $ hg help multirevs
354 Specifying Multiple Revisions
354 Specifying Multiple Revisions
355
355
356 When Mercurial accepts more than one revision, they may be specified
356 When Mercurial accepts more than one revision, they may be specified
357 individually, or provided as a topologically continuous range, separated
357 individually, or provided as a topologically continuous range, separated
358 by the ":" character.
358 by the ":" character.
359
359
360 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
360 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
361 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
361 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
362 specified, it defaults to revision number 0. If END is not specified, it
362 specified, it defaults to revision number 0. If END is not specified, it
363 defaults to the tip. The range ":" thus means "all revisions".
363 defaults to the tip. The range ":" thus means "all revisions".
364
364
365 If BEGIN is greater than END, revisions are treated in reverse order.
365 If BEGIN is greater than END, revisions are treated in reverse order.
366
366
367 A range acts as a closed interval. This means that a range of 3:5 gives 3,
367 A range acts as a closed interval. This means that a range of 3:5 gives 3,
368 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
368 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
369
369
370 use "hg help -c multirevs" to see help for the multirevs command
370 use "hg help -c multirevs" to see help for the multirevs command
371
371
372 $ hg help -c multirevs
372 $ hg help -c multirevs
373 hg multirevs ARG
373 hg multirevs ARG
374
374
375 multirevs command
375 multirevs command
376
376
377 use "hg -v help multirevs" to show more info
377 use "hg -v help multirevs" to show more info
378
378
379 $ hg multirevs
379 $ hg multirevs
380 hg multirevs: invalid arguments
380 hg multirevs: invalid arguments
381 hg multirevs ARG
381 hg multirevs ARG
382
382
383 multirevs command
383 multirevs command
384
384
385 use "hg help multirevs" to show the full help text
385 use "hg help multirevs" to show the full help text
386 [255]
386 [255]
387
387
388 $ echo "multirevs = !" >> $HGRCPATH
388 $ echo "multirevs = !" >> $HGRCPATH
389
389
390 Issue811: Problem loading extensions twice (by site and by user)
390 Issue811: Problem loading extensions twice (by site and by user)
391
391
392 $ debugpath=`pwd`/debugissue811.py
392 $ debugpath=`pwd`/debugissue811.py
393 $ cat > debugissue811.py <<EOF
393 $ cat > debugissue811.py <<EOF
394 > '''show all loaded extensions
394 > '''show all loaded extensions
395 > '''
395 > '''
396 > from mercurial import extensions, commands
396 > from mercurial import extensions, commands
397 >
397 >
398 > def debugextensions(ui):
398 > def debugextensions(ui):
399 > "yet another debug command"
399 > "yet another debug command"
400 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
400 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
401 >
401 >
402 > cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
402 > cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
403 > commands.norepo += " debugextensions"
403 > commands.norepo += " debugextensions"
404 > EOF
404 > EOF
405 $ echo "debugissue811 = $debugpath" >> $HGRCPATH
405 $ echo "debugissue811 = $debugpath" >> $HGRCPATH
406 $ echo "mq=" >> $HGRCPATH
406 $ echo "mq=" >> $HGRCPATH
407 $ echo "hgext.mq=" >> $HGRCPATH
407 $ echo "hgext.mq=" >> $HGRCPATH
408 $ echo "hgext/mq=" >> $HGRCPATH
408 $ echo "hgext/mq=" >> $HGRCPATH
409
409
410 Show extensions:
410 Show extensions:
411
411
412 $ hg debugextensions
412 $ hg debugextensions
413 debugissue811
413 debugissue811
414 mq
414 mq
415
415
416 Disabled extension commands:
416 Disabled extension commands:
417
417
418 $ HGRCPATH=
418 $ HGRCPATH=
419 $ export HGRCPATH
419 $ export HGRCPATH
420 $ hg help email
420 $ hg help email
421 'email' is provided by the following extension:
421 'email' is provided by the following extension:
422
422
423 patchbomb command to send changesets as (a series of) patch emails
423 patchbomb command to send changesets as (a series of) patch emails
424
424
425 use "hg help extensions" for information on enabling extensions
425 use "hg help extensions" for information on enabling extensions
426 $ hg qdel
426 $ hg qdel
427 hg: unknown command 'qdel'
427 hg: unknown command 'qdel'
428 'qdelete' is provided by the following extension:
428 'qdelete' is provided by the following extension:
429
429
430 mq manage a stack of patches
430 mq manage a stack of patches
431
431
432 use "hg help extensions" for information on enabling extensions
432 use "hg help extensions" for information on enabling extensions
433 [255]
433 [255]
434 $ hg churn
434 $ hg churn
435 hg: unknown command 'churn'
435 hg: unknown command 'churn'
436 'churn' is provided by the following extension:
436 'churn' is provided by the following extension:
437
437
438 churn command to display statistics about repository history
438 churn command to display statistics about repository history
439
439
440 use "hg help extensions" for information on enabling extensions
440 use "hg help extensions" for information on enabling extensions
441 [255]
441 [255]
442
442
443 Disabled extensions:
443 Disabled extensions:
444
444
445 $ hg help churn
445 $ hg help churn
446 churn extension - command to display statistics about repository history
446 churn extension - command to display statistics about repository history
447
447
448 use "hg help extensions" for information on enabling extensions
448 use "hg help extensions" for information on enabling extensions
449 $ hg help patchbomb
449 $ hg help patchbomb
450 patchbomb extension - command to send changesets as (a series of) patch emails
450 patchbomb extension - command to send changesets as (a series of) patch emails
451
451
452 use "hg help extensions" for information on enabling extensions
452 use "hg help extensions" for information on enabling extensions
453
453
454 Broken disabled extension and command:
454 Broken disabled extension and command:
455
455
456 $ mkdir hgext
456 $ mkdir hgext
457 $ echo > hgext/__init__.py
457 $ echo > hgext/__init__.py
458 $ cat > hgext/broken.py <<EOF
458 $ cat > hgext/broken.py <<EOF
459 > "broken extension'
459 > "broken extension'
460 > EOF
460 > EOF
461 $ cat > path.py <<EOF
461 $ cat > path.py <<EOF
462 > import os, sys
462 > import os, sys
463 > sys.path.insert(0, os.environ['HGEXTPATH'])
463 > sys.path.insert(0, os.environ['HGEXTPATH'])
464 > EOF
464 > EOF
465 $ HGEXTPATH=`pwd`
465 $ HGEXTPATH=`pwd`
466 $ export HGEXTPATH
466 $ export HGEXTPATH
467
467
468 $ hg --config extensions.path=./path.py help broken
468 $ hg --config extensions.path=./path.py help broken
469 broken extension - (no help text available)
469 broken extension - (no help text available)
470
470
471 use "hg help extensions" for information on enabling extensions
471 use "hg help extensions" for information on enabling extensions
472
472
473 $ cat > hgext/forest.py <<EOF
473 $ cat > hgext/forest.py <<EOF
474 > cmdtable = None
474 > cmdtable = None
475 > EOF
475 > EOF
476 $ hg --config extensions.path=./path.py help foo > /dev/null
476 $ hg --config extensions.path=./path.py help foo > /dev/null
477 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
477 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
478 hg: unknown command 'foo'
478 hg: unknown command 'foo'
479 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
479 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
480 [255]
480 [255]
481
482 $ cat > throw.py <<EOF
483 > from mercurial import cmdutil, commands
484 > cmdtable = {}
485 > command = cmdutil.command(cmdtable)
486 > class Bogon(Exception): pass
487 >
488 > @command('throw', [], 'hg throw')
489 > def throw(ui, **opts):
490 > """throws an exception"""
491 > raise Bogon()
492 > commands.norepo += " throw"
493 > EOF
494 No declared supported version, extension complains:
495 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
496 ** Unknown exception encountered with possibly-broken third-party extension throw
497 ** which supports versions unknown of Mercurial.
498 ** Please disable throw and try your action again.
499 ** If that fixes the bug please report it to the extension author.
500 ** Python * (glob)
501 ** Mercurial Distributed SCM * (glob)
502 ** Extensions loaded: throw
503 If the extension specifies a buglink, show that:
504 $ echo 'buglink = "http://example.com/bts"' >> throw.py
505 $ rm -f throw.pyc throw.pyo
506 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
507 ** Unknown exception encountered with possibly-broken third-party extension throw
508 ** which supports versions unknown of Mercurial.
509 ** Please disable throw and try your action again.
510 ** If that fixes the bug please report it to http://example.com/bts
511 ** Python * (glob)
512 ** Mercurial Distributed SCM (*) (glob)
513 ** Extensions loaded: throw
514 If the extensions declare outdated versions, accuse the older extension first:
515 $ echo "testedwith = '1.9.3'" >> older.py
516 $ echo "testedwith = '2.1.1'" >> throw.py
517 $ rm -f throw.pyc throw.pyo
518 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
519 > throw 2>&1 | egrep '^\*\*'
520 ** Unknown exception encountered with possibly-broken third-party extension older
521 ** which supports versions 1.9.3 of Mercurial.
522 ** Please disable older and try your action again.
523 ** If that fixes the bug please report it to the extension author.
524 ** Python * (glob)
525 ** Mercurial Distributed SCM (*) (glob)
526 ** Extensions loaded: throw, older
527
528 Declare the version as supporting this hg version, show regular bts link:
529 $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'`
530 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
531 $ rm -f throw.pyc throw.pyo
532 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
533 ** unknown exception encountered, please report by visiting
534 ** http://mercurial.selenic.com/wiki/BugTracker
535 ** Python * (glob)
536 ** Mercurial Distributed SCM (*) (glob)
537 ** Extensions loaded: throw
General Comments 0
You need to be logged in to leave comments. Login now