##// END OF EJS Templates
alias: keep error message in "badalias" so that help can see it...
Yuya Nishihara -
r22160:645457f7 default
parent child Browse files
Show More
@@ -1,929 +1,917 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', '--traceback')
43 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
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 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
65 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
66 starttime = time.time()
66 starttime = time.time()
67 ret = None
67 ret = None
68 try:
68 try:
69 ret = _runcatch(req)
69 ret = _runcatch(req)
70 return ret
70 return ret
71 finally:
71 finally:
72 duration = time.time() - starttime
72 duration = time.time() - starttime
73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
74 msg, ret or 0, duration)
74 msg, ret or 0, duration)
75
75
76 def _runcatch(req):
76 def _runcatch(req):
77 def catchterm(*args):
77 def catchterm(*args):
78 raise error.SignalInterrupt
78 raise error.SignalInterrupt
79
79
80 ui = req.ui
80 ui = req.ui
81 try:
81 try:
82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
83 num = getattr(signal, name, None)
83 num = getattr(signal, name, None)
84 if num:
84 if num:
85 signal.signal(num, catchterm)
85 signal.signal(num, catchterm)
86 except ValueError:
86 except ValueError:
87 pass # happens if called in a thread
87 pass # happens if called in a thread
88
88
89 try:
89 try:
90 try:
90 try:
91 debugger = 'pdb'
91 debugger = 'pdb'
92 debugtrace = {
92 debugtrace = {
93 'pdb' : pdb.set_trace
93 'pdb' : pdb.set_trace
94 }
94 }
95 debugmortem = {
95 debugmortem = {
96 'pdb' : pdb.post_mortem
96 'pdb' : pdb.post_mortem
97 }
97 }
98
98
99 # read --config before doing anything else
99 # read --config before doing anything else
100 # (e.g. to change trust settings for reading .hg/hgrc)
100 # (e.g. to change trust settings for reading .hg/hgrc)
101 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
101 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
102
102
103 if req.repo:
103 if req.repo:
104 # copy configs that were passed on the cmdline (--config) to
104 # copy configs that were passed on the cmdline (--config) to
105 # the repo ui
105 # the repo ui
106 for sec, name, val in cfgs:
106 for sec, name, val in cfgs:
107 req.repo.ui.setconfig(sec, name, val, source='--config')
107 req.repo.ui.setconfig(sec, name, val, source='--config')
108
108
109 # if we are in HGPLAIN mode, then disable custom debugging
109 # if we are in HGPLAIN mode, then disable custom debugging
110 debugger = ui.config("ui", "debugger")
110 debugger = ui.config("ui", "debugger")
111 debugmod = pdb
111 debugmod = pdb
112 if not debugger or ui.plain():
112 if not debugger or ui.plain():
113 debugger = 'pdb'
113 debugger = 'pdb'
114 elif '--debugger' in req.args:
114 elif '--debugger' in req.args:
115 # This import can be slow for fancy debuggers, so only
115 # This import can be slow for fancy debuggers, so only
116 # do it when absolutely necessary, i.e. when actual
116 # do it when absolutely necessary, i.e. when actual
117 # debugging has been requested
117 # debugging has been requested
118 try:
118 try:
119 debugmod = __import__(debugger)
119 debugmod = __import__(debugger)
120 except ImportError:
120 except ImportError:
121 pass # Leave debugmod = pdb
121 pass # Leave debugmod = pdb
122
122
123 debugtrace[debugger] = debugmod.set_trace
123 debugtrace[debugger] = debugmod.set_trace
124 debugmortem[debugger] = debugmod.post_mortem
124 debugmortem[debugger] = debugmod.post_mortem
125
125
126 # enter the debugger before command execution
126 # enter the debugger before command execution
127 if '--debugger' in req.args:
127 if '--debugger' in req.args:
128 ui.warn(_("entering debugger - "
128 ui.warn(_("entering debugger - "
129 "type c to continue starting hg or h for help\n"))
129 "type c to continue starting hg or h for help\n"))
130
130
131 if (debugger != 'pdb' and
131 if (debugger != 'pdb' and
132 debugtrace[debugger] == debugtrace['pdb']):
132 debugtrace[debugger] == debugtrace['pdb']):
133 ui.warn(_("%s debugger specified "
133 ui.warn(_("%s debugger specified "
134 "but its module was not found\n") % debugger)
134 "but its module was not found\n") % debugger)
135
135
136 debugtrace[debugger]()
136 debugtrace[debugger]()
137 try:
137 try:
138 return _dispatch(req)
138 return _dispatch(req)
139 finally:
139 finally:
140 ui.flush()
140 ui.flush()
141 except: # re-raises
141 except: # re-raises
142 # enter the debugger when we hit an exception
142 # enter the debugger when we hit an exception
143 if '--debugger' in req.args:
143 if '--debugger' in req.args:
144 traceback.print_exc()
144 traceback.print_exc()
145 debugmortem[debugger](sys.exc_info()[2])
145 debugmortem[debugger](sys.exc_info()[2])
146 ui.traceback()
146 ui.traceback()
147 raise
147 raise
148
148
149 # Global exception handling, alphabetically
149 # Global exception handling, alphabetically
150 # Mercurial-specific first, followed by built-in and library exceptions
150 # Mercurial-specific first, followed by built-in and library exceptions
151 except error.AmbiguousCommand, inst:
151 except error.AmbiguousCommand, inst:
152 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
152 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
153 (inst.args[0], " ".join(inst.args[1])))
153 (inst.args[0], " ".join(inst.args[1])))
154 except error.ParseError, inst:
154 except error.ParseError, inst:
155 if len(inst.args) > 1:
155 if len(inst.args) > 1:
156 ui.warn(_("hg: parse error at %s: %s\n") %
156 ui.warn(_("hg: parse error at %s: %s\n") %
157 (inst.args[1], inst.args[0]))
157 (inst.args[1], inst.args[0]))
158 else:
158 else:
159 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
159 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
160 return -1
160 return -1
161 except error.LockHeld, inst:
161 except error.LockHeld, inst:
162 if inst.errno == errno.ETIMEDOUT:
162 if inst.errno == errno.ETIMEDOUT:
163 reason = _('timed out waiting for lock held by %s') % inst.locker
163 reason = _('timed out waiting for lock held by %s') % inst.locker
164 else:
164 else:
165 reason = _('lock held by %s') % inst.locker
165 reason = _('lock held by %s') % inst.locker
166 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
166 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
167 except error.LockUnavailable, inst:
167 except error.LockUnavailable, inst:
168 ui.warn(_("abort: could not lock %s: %s\n") %
168 ui.warn(_("abort: could not lock %s: %s\n") %
169 (inst.desc or inst.filename, inst.strerror))
169 (inst.desc or inst.filename, inst.strerror))
170 except error.CommandError, inst:
170 except error.CommandError, inst:
171 if inst.args[0]:
171 if inst.args[0]:
172 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
172 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
173 commands.help_(ui, inst.args[0], full=False, command=True)
173 commands.help_(ui, inst.args[0], full=False, command=True)
174 else:
174 else:
175 ui.warn(_("hg: %s\n") % inst.args[1])
175 ui.warn(_("hg: %s\n") % inst.args[1])
176 commands.help_(ui, 'shortlist')
176 commands.help_(ui, 'shortlist')
177 except error.OutOfBandError, inst:
177 except error.OutOfBandError, inst:
178 ui.warn(_("abort: remote error:\n"))
178 ui.warn(_("abort: remote error:\n"))
179 ui.warn(''.join(inst.args))
179 ui.warn(''.join(inst.args))
180 except error.RepoError, inst:
180 except error.RepoError, inst:
181 ui.warn(_("abort: %s!\n") % inst)
181 ui.warn(_("abort: %s!\n") % inst)
182 if inst.hint:
182 if inst.hint:
183 ui.warn(_("(%s)\n") % inst.hint)
183 ui.warn(_("(%s)\n") % inst.hint)
184 except error.ResponseError, inst:
184 except error.ResponseError, inst:
185 ui.warn(_("abort: %s") % inst.args[0])
185 ui.warn(_("abort: %s") % inst.args[0])
186 if not isinstance(inst.args[1], basestring):
186 if not isinstance(inst.args[1], basestring):
187 ui.warn(" %r\n" % (inst.args[1],))
187 ui.warn(" %r\n" % (inst.args[1],))
188 elif not inst.args[1]:
188 elif not inst.args[1]:
189 ui.warn(_(" empty string\n"))
189 ui.warn(_(" empty string\n"))
190 else:
190 else:
191 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
191 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
192 except error.RevlogError, inst:
192 except error.RevlogError, inst:
193 ui.warn(_("abort: %s!\n") % inst)
193 ui.warn(_("abort: %s!\n") % inst)
194 except error.SignalInterrupt:
194 except error.SignalInterrupt:
195 ui.warn(_("killed!\n"))
195 ui.warn(_("killed!\n"))
196 except error.UnknownCommand, inst:
196 except error.UnknownCommand, inst:
197 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
197 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
198 try:
198 try:
199 # check if the command is in a disabled extension
199 # check if the command is in a disabled extension
200 # (but don't check for extensions themselves)
200 # (but don't check for extensions themselves)
201 commands.help_(ui, inst.args[0], unknowncmd=True)
201 commands.help_(ui, inst.args[0], unknowncmd=True)
202 except error.UnknownCommand:
202 except error.UnknownCommand:
203 commands.help_(ui, 'shortlist')
203 commands.help_(ui, 'shortlist')
204 except error.InterventionRequired, inst:
204 except error.InterventionRequired, inst:
205 ui.warn("%s\n" % inst)
205 ui.warn("%s\n" % inst)
206 return 1
206 return 1
207 except util.Abort, inst:
207 except util.Abort, inst:
208 ui.warn(_("abort: %s\n") % inst)
208 ui.warn(_("abort: %s\n") % inst)
209 if inst.hint:
209 if inst.hint:
210 ui.warn(_("(%s)\n") % inst.hint)
210 ui.warn(_("(%s)\n") % inst.hint)
211 except ImportError, inst:
211 except ImportError, inst:
212 ui.warn(_("abort: %s!\n") % inst)
212 ui.warn(_("abort: %s!\n") % inst)
213 m = str(inst).split()[-1]
213 m = str(inst).split()[-1]
214 if m in "mpatch bdiff".split():
214 if m in "mpatch bdiff".split():
215 ui.warn(_("(did you forget to compile extensions?)\n"))
215 ui.warn(_("(did you forget to compile extensions?)\n"))
216 elif m in "zlib".split():
216 elif m in "zlib".split():
217 ui.warn(_("(is your Python install correct?)\n"))
217 ui.warn(_("(is your Python install correct?)\n"))
218 except IOError, inst:
218 except IOError, inst:
219 if util.safehasattr(inst, "code"):
219 if util.safehasattr(inst, "code"):
220 ui.warn(_("abort: %s\n") % inst)
220 ui.warn(_("abort: %s\n") % inst)
221 elif util.safehasattr(inst, "reason"):
221 elif util.safehasattr(inst, "reason"):
222 try: # usually it is in the form (errno, strerror)
222 try: # usually it is in the form (errno, strerror)
223 reason = inst.reason.args[1]
223 reason = inst.reason.args[1]
224 except (AttributeError, IndexError):
224 except (AttributeError, IndexError):
225 # it might be anything, for example a string
225 # it might be anything, for example a string
226 reason = inst.reason
226 reason = inst.reason
227 ui.warn(_("abort: error: %s\n") % reason)
227 ui.warn(_("abort: error: %s\n") % reason)
228 elif (util.safehasattr(inst, "args")
228 elif (util.safehasattr(inst, "args")
229 and inst.args and inst.args[0] == errno.EPIPE):
229 and inst.args and inst.args[0] == errno.EPIPE):
230 if ui.debugflag:
230 if ui.debugflag:
231 ui.warn(_("broken pipe\n"))
231 ui.warn(_("broken pipe\n"))
232 elif getattr(inst, "strerror", None):
232 elif getattr(inst, "strerror", None):
233 if getattr(inst, "filename", None):
233 if getattr(inst, "filename", None):
234 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
234 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
235 else:
235 else:
236 ui.warn(_("abort: %s\n") % inst.strerror)
236 ui.warn(_("abort: %s\n") % inst.strerror)
237 else:
237 else:
238 raise
238 raise
239 except OSError, inst:
239 except OSError, inst:
240 if getattr(inst, "filename", None) is not None:
240 if getattr(inst, "filename", None) is not None:
241 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
241 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
242 else:
242 else:
243 ui.warn(_("abort: %s\n") % inst.strerror)
243 ui.warn(_("abort: %s\n") % inst.strerror)
244 except KeyboardInterrupt:
244 except KeyboardInterrupt:
245 try:
245 try:
246 ui.warn(_("interrupted!\n"))
246 ui.warn(_("interrupted!\n"))
247 except IOError, inst:
247 except IOError, inst:
248 if inst.errno == errno.EPIPE:
248 if inst.errno == errno.EPIPE:
249 if ui.debugflag:
249 if ui.debugflag:
250 ui.warn(_("\nbroken pipe\n"))
250 ui.warn(_("\nbroken pipe\n"))
251 else:
251 else:
252 raise
252 raise
253 except MemoryError:
253 except MemoryError:
254 ui.warn(_("abort: out of memory\n"))
254 ui.warn(_("abort: out of memory\n"))
255 except SystemExit, inst:
255 except SystemExit, inst:
256 # Commands shouldn't sys.exit directly, but give a return code.
256 # Commands shouldn't sys.exit directly, but give a return code.
257 # Just in case catch this and and pass exit code to caller.
257 # Just in case catch this and and pass exit code to caller.
258 return inst.code
258 return inst.code
259 except socket.error, inst:
259 except socket.error, inst:
260 ui.warn(_("abort: %s\n") % inst.args[-1])
260 ui.warn(_("abort: %s\n") % inst.args[-1])
261 except: # re-raises
261 except: # re-raises
262 myver = util.version()
262 myver = util.version()
263 # For compatibility checking, we discard the portion of the hg
263 # For compatibility checking, we discard the portion of the hg
264 # version after the + on the assumption that if a "normal
264 # version after the + on the assumption that if a "normal
265 # user" is running a build with a + in it the packager
265 # user" is running a build with a + in it the packager
266 # probably built from fairly close to a tag and anyone with a
266 # probably built from fairly close to a tag and anyone with a
267 # 'make local' copy of hg (where the version number can be out
267 # 'make local' copy of hg (where the version number can be out
268 # of date) will be clueful enough to notice the implausible
268 # of date) will be clueful enough to notice the implausible
269 # version number and try updating.
269 # version number and try updating.
270 compare = myver.split('+')[0]
270 compare = myver.split('+')[0]
271 ct = tuplever(compare)
271 ct = tuplever(compare)
272 worst = None, ct, ''
272 worst = None, ct, ''
273 for name, mod in extensions.extensions():
273 for name, mod in extensions.extensions():
274 testedwith = getattr(mod, 'testedwith', '')
274 testedwith = getattr(mod, 'testedwith', '')
275 report = getattr(mod, 'buglink', _('the extension author.'))
275 report = getattr(mod, 'buglink', _('the extension author.'))
276 if not testedwith.strip():
276 if not testedwith.strip():
277 # We found an untested extension. It's likely the culprit.
277 # We found an untested extension. It's likely the culprit.
278 worst = name, 'unknown', report
278 worst = name, 'unknown', report
279 break
279 break
280 if compare not in testedwith.split() and testedwith != 'internal':
280 if compare not in testedwith.split() and testedwith != 'internal':
281 tested = [tuplever(v) for v in testedwith.split()]
281 tested = [tuplever(v) for v in testedwith.split()]
282 lower = [t for t in tested if t < ct]
282 lower = [t for t in tested if t < ct]
283 nearest = max(lower or tested)
283 nearest = max(lower or tested)
284 if worst[0] is None or nearest < worst[1]:
284 if worst[0] is None or nearest < worst[1]:
285 worst = name, nearest, report
285 worst = name, nearest, report
286 if worst[0] is not None:
286 if worst[0] is not None:
287 name, testedwith, report = worst
287 name, testedwith, report = worst
288 if not isinstance(testedwith, str):
288 if not isinstance(testedwith, str):
289 testedwith = '.'.join([str(c) for c in testedwith])
289 testedwith = '.'.join([str(c) for c in testedwith])
290 warning = (_('** Unknown exception encountered with '
290 warning = (_('** Unknown exception encountered with '
291 'possibly-broken third-party extension %s\n'
291 'possibly-broken third-party extension %s\n'
292 '** which supports versions %s of Mercurial.\n'
292 '** which supports versions %s of Mercurial.\n'
293 '** Please disable %s and try your action again.\n'
293 '** Please disable %s and try your action again.\n'
294 '** If that fixes the bug please report it to %s\n')
294 '** If that fixes the bug please report it to %s\n')
295 % (name, testedwith, name, report))
295 % (name, testedwith, name, report))
296 else:
296 else:
297 warning = (_("** unknown exception encountered, "
297 warning = (_("** unknown exception encountered, "
298 "please report by visiting\n") +
298 "please report by visiting\n") +
299 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
299 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
300 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
300 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
301 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
301 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
302 (_("** Extensions loaded: %s\n") %
302 (_("** Extensions loaded: %s\n") %
303 ", ".join([x[0] for x in extensions.extensions()])))
303 ", ".join([x[0] for x in extensions.extensions()])))
304 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
304 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
305 ui.warn(warning)
305 ui.warn(warning)
306 raise
306 raise
307
307
308 return -1
308 return -1
309
309
310 def tuplever(v):
310 def tuplever(v):
311 try:
311 try:
312 return tuple([int(i) for i in v.split('.')])
312 return tuple([int(i) for i in v.split('.')])
313 except ValueError:
313 except ValueError:
314 return tuple()
314 return tuple()
315
315
316 def aliasargs(fn, givenargs):
316 def aliasargs(fn, givenargs):
317 args = getattr(fn, 'args', [])
317 args = getattr(fn, 'args', [])
318 if args:
318 if args:
319 cmd = ' '.join(map(util.shellquote, args))
319 cmd = ' '.join(map(util.shellquote, args))
320
320
321 nums = []
321 nums = []
322 def replacer(m):
322 def replacer(m):
323 num = int(m.group(1)) - 1
323 num = int(m.group(1)) - 1
324 nums.append(num)
324 nums.append(num)
325 if num < len(givenargs):
325 if num < len(givenargs):
326 return givenargs[num]
326 return givenargs[num]
327 raise util.Abort(_('too few arguments for command alias'))
327 raise util.Abort(_('too few arguments for command alias'))
328 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
328 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
329 givenargs = [x for i, x in enumerate(givenargs)
329 givenargs = [x for i, x in enumerate(givenargs)
330 if i not in nums]
330 if i not in nums]
331 args = shlex.split(cmd)
331 args = shlex.split(cmd)
332 return args + givenargs
332 return args + givenargs
333
333
334 def aliasinterpolate(name, args, cmd):
334 def aliasinterpolate(name, args, cmd):
335 '''interpolate args into cmd for shell aliases
335 '''interpolate args into cmd for shell aliases
336
336
337 This also handles $0, $@ and "$@".
337 This also handles $0, $@ and "$@".
338 '''
338 '''
339 # util.interpolate can't deal with "$@" (with quotes) because it's only
339 # util.interpolate can't deal with "$@" (with quotes) because it's only
340 # built to match prefix + patterns.
340 # built to match prefix + patterns.
341 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
341 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
342 replacemap['$0'] = name
342 replacemap['$0'] = name
343 replacemap['$$'] = '$'
343 replacemap['$$'] = '$'
344 replacemap['$@'] = ' '.join(args)
344 replacemap['$@'] = ' '.join(args)
345 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
345 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
346 # parameters, separated out into words. Emulate the same behavior here by
346 # parameters, separated out into words. Emulate the same behavior here by
347 # quoting the arguments individually. POSIX shells will then typically
347 # quoting the arguments individually. POSIX shells will then typically
348 # tokenize each argument into exactly one word.
348 # tokenize each argument into exactly one word.
349 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
349 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
350 # escape '\$' for regex
350 # escape '\$' for regex
351 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
351 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
352 r = re.compile(regex)
352 r = re.compile(regex)
353 return r.sub(lambda x: replacemap[x.group()], cmd)
353 return r.sub(lambda x: replacemap[x.group()], cmd)
354
354
355 class cmdalias(object):
355 class cmdalias(object):
356 def __init__(self, name, definition, cmdtable):
356 def __init__(self, name, definition, cmdtable):
357 self.name = self.cmd = name
357 self.name = self.cmd = name
358 self.cmdname = ''
358 self.cmdname = ''
359 self.definition = definition
359 self.definition = definition
360 self.fn = None
360 self.args = []
361 self.args = []
361 self.opts = []
362 self.opts = []
362 self.help = ''
363 self.help = ''
363 self.norepo = True
364 self.norepo = True
364 self.optionalrepo = False
365 self.optionalrepo = False
365 self.badalias = False
366 self.badalias = None
366
367
367 try:
368 try:
368 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
369 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
369 for alias, e in cmdtable.iteritems():
370 for alias, e in cmdtable.iteritems():
370 if e is entry:
371 if e is entry:
371 self.cmd = alias
372 self.cmd = alias
372 break
373 break
373 self.shadows = True
374 self.shadows = True
374 except error.UnknownCommand:
375 except error.UnknownCommand:
375 self.shadows = False
376 self.shadows = False
376
377
377 if not self.definition:
378 if not self.definition:
378 def fn(ui, *args):
379 self.badalias = _("no definition for alias '%s'") % self.name
379 ui.warn(_("no definition for alias '%s'\n") % self.name)
380 return -1
381 self.fn = fn
382 self.badalias = True
383 return
380 return
384
381
385 if self.definition.startswith('!'):
382 if self.definition.startswith('!'):
386 self.shell = True
383 self.shell = True
387 def fn(ui, *args):
384 def fn(ui, *args):
388 env = {'HG_ARGS': ' '.join((self.name,) + args)}
385 env = {'HG_ARGS': ' '.join((self.name,) + args)}
389 def _checkvar(m):
386 def _checkvar(m):
390 if m.groups()[0] == '$':
387 if m.groups()[0] == '$':
391 return m.group()
388 return m.group()
392 elif int(m.groups()[0]) <= len(args):
389 elif int(m.groups()[0]) <= len(args):
393 return m.group()
390 return m.group()
394 else:
391 else:
395 ui.debug("No argument found for substitution "
392 ui.debug("No argument found for substitution "
396 "of %i variable in alias '%s' definition."
393 "of %i variable in alias '%s' definition."
397 % (int(m.groups()[0]), self.name))
394 % (int(m.groups()[0]), self.name))
398 return ''
395 return ''
399 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
396 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
400 cmd = aliasinterpolate(self.name, args, cmd)
397 cmd = aliasinterpolate(self.name, args, cmd)
401 return util.system(cmd, environ=env, out=ui.fout)
398 return util.system(cmd, environ=env, out=ui.fout)
402 self.fn = fn
399 self.fn = fn
403 return
400 return
404
401
405 try:
402 try:
406 args = shlex.split(self.definition)
403 args = shlex.split(self.definition)
407 except ValueError, inst:
404 except ValueError, inst:
408 def fn(ui, *args):
405 self.badalias = (_("error in definition for alias '%s': %s")
409 ui.warn(_("error in definition for alias '%s': %s\n")
410 % (self.name, inst))
406 % (self.name, inst))
411 return -1
412 self.fn = fn
413 self.badalias = True
414 return
407 return
415 self.cmdname = cmd = args.pop(0)
408 self.cmdname = cmd = args.pop(0)
416 args = map(util.expandpath, args)
409 args = map(util.expandpath, args)
417
410
418 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
411 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
419 if _earlygetopt([invalidarg], args):
412 if _earlygetopt([invalidarg], args):
420 def fn(ui, *args):
413 self.badalias = (_("error in definition for alias '%s': %s may "
421 ui.warn(_("error in definition for alias '%s': %s may only "
414 "only be given on the command line")
422 "be given on the command line\n")
423 % (self.name, invalidarg))
415 % (self.name, invalidarg))
424 return -1
425
426 self.fn = fn
427 self.badalias = True
428 return
416 return
429
417
430 try:
418 try:
431 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
419 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
432 if len(tableentry) > 2:
420 if len(tableentry) > 2:
433 self.fn, self.opts, self.help = tableentry
421 self.fn, self.opts, self.help = tableentry
434 else:
422 else:
435 self.fn, self.opts = tableentry
423 self.fn, self.opts = tableentry
436
424
437 self.args = aliasargs(self.fn, args)
425 self.args = aliasargs(self.fn, args)
438 if cmd not in commands.norepo.split(' '):
426 if cmd not in commands.norepo.split(' '):
439 self.norepo = False
427 self.norepo = False
440 if cmd in commands.optionalrepo.split(' '):
428 if cmd in commands.optionalrepo.split(' '):
441 self.optionalrepo = True
429 self.optionalrepo = True
442 if self.help.startswith("hg " + cmd):
430 if self.help.startswith("hg " + cmd):
443 # drop prefix in old-style help lines so hg shows the alias
431 # drop prefix in old-style help lines so hg shows the alias
444 self.help = self.help[4 + len(cmd):]
432 self.help = self.help[4 + len(cmd):]
445 self.__doc__ = self.fn.__doc__
433 self.__doc__ = self.fn.__doc__
446
434
447 except error.UnknownCommand:
435 except error.UnknownCommand:
448 def fn(ui, *args):
436 def fn(ui, *args):
449 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
450 % (self.name, cmd))
451 try:
437 try:
452 # check if the command is in a disabled extension
438 # check if the command is in a disabled extension
453 commands.help_(ui, cmd, unknowncmd=True)
439 commands.help_(ui, cmd, unknowncmd=True)
454 except error.UnknownCommand:
440 except error.UnknownCommand:
455 pass
441 pass
456 return -1
442 return -1
457 self.fn = fn
443 self.fn = fn
458 self.badalias = True
444 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
445 % (self.name, cmd))
459 except error.AmbiguousCommand:
446 except error.AmbiguousCommand:
460 def fn(ui, *args):
447 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
461 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
462 % (self.name, cmd))
448 % (self.name, cmd))
463 return -1
464 self.fn = fn
465 self.badalias = True
466
449
467 def __call__(self, ui, *args, **opts):
450 def __call__(self, ui, *args, **opts):
451 if self.badalias:
452 ui.warn(self.badalias + '\n')
453 if self.fn:
454 return self.fn(ui, *args, **opts)
455 return -1
468 if self.shadows:
456 if self.shadows:
469 ui.debug("alias '%s' shadows command '%s'\n" %
457 ui.debug("alias '%s' shadows command '%s'\n" %
470 (self.name, self.cmdname))
458 (self.name, self.cmdname))
471
459
472 if util.safehasattr(self, 'shell'):
460 if util.safehasattr(self, 'shell'):
473 return self.fn(ui, *args, **opts)
461 return self.fn(ui, *args, **opts)
474 else:
462 else:
475 try:
463 try:
476 return util.checksignature(self.fn)(ui, *args, **opts)
464 return util.checksignature(self.fn)(ui, *args, **opts)
477 except error.SignatureError:
465 except error.SignatureError:
478 args = ' '.join([self.cmdname] + self.args)
466 args = ' '.join([self.cmdname] + self.args)
479 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
467 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
480 raise
468 raise
481
469
482 def addaliases(ui, cmdtable):
470 def addaliases(ui, cmdtable):
483 # aliases are processed after extensions have been loaded, so they
471 # aliases are processed after extensions have been loaded, so they
484 # may use extension commands. Aliases can also use other alias definitions,
472 # may use extension commands. Aliases can also use other alias definitions,
485 # but only if they have been defined prior to the current definition.
473 # but only if they have been defined prior to the current definition.
486 for alias, definition in ui.configitems('alias'):
474 for alias, definition in ui.configitems('alias'):
487 aliasdef = cmdalias(alias, definition, cmdtable)
475 aliasdef = cmdalias(alias, definition, cmdtable)
488
476
489 try:
477 try:
490 olddef = cmdtable[aliasdef.cmd][0]
478 olddef = cmdtable[aliasdef.cmd][0]
491 if olddef.definition == aliasdef.definition:
479 if olddef.definition == aliasdef.definition:
492 continue
480 continue
493 except (KeyError, AttributeError):
481 except (KeyError, AttributeError):
494 # definition might not exist or it might not be a cmdalias
482 # definition might not exist or it might not be a cmdalias
495 pass
483 pass
496
484
497 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
485 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
498 if aliasdef.norepo:
486 if aliasdef.norepo:
499 commands.norepo += ' %s' % alias
487 commands.norepo += ' %s' % alias
500 if aliasdef.optionalrepo:
488 if aliasdef.optionalrepo:
501 commands.optionalrepo += ' %s' % alias
489 commands.optionalrepo += ' %s' % alias
502
490
503 def _parse(ui, args):
491 def _parse(ui, args):
504 options = {}
492 options = {}
505 cmdoptions = {}
493 cmdoptions = {}
506
494
507 try:
495 try:
508 args = fancyopts.fancyopts(args, commands.globalopts, options)
496 args = fancyopts.fancyopts(args, commands.globalopts, options)
509 except fancyopts.getopt.GetoptError, inst:
497 except fancyopts.getopt.GetoptError, inst:
510 raise error.CommandError(None, inst)
498 raise error.CommandError(None, inst)
511
499
512 if args:
500 if args:
513 cmd, args = args[0], args[1:]
501 cmd, args = args[0], args[1:]
514 aliases, entry = cmdutil.findcmd(cmd, commands.table,
502 aliases, entry = cmdutil.findcmd(cmd, commands.table,
515 ui.configbool("ui", "strict"))
503 ui.configbool("ui", "strict"))
516 cmd = aliases[0]
504 cmd = aliases[0]
517 args = aliasargs(entry[0], args)
505 args = aliasargs(entry[0], args)
518 defaults = ui.config("defaults", cmd)
506 defaults = ui.config("defaults", cmd)
519 if defaults:
507 if defaults:
520 args = map(util.expandpath, shlex.split(defaults)) + args
508 args = map(util.expandpath, shlex.split(defaults)) + args
521 c = list(entry[1])
509 c = list(entry[1])
522 else:
510 else:
523 cmd = None
511 cmd = None
524 c = []
512 c = []
525
513
526 # combine global options into local
514 # combine global options into local
527 for o in commands.globalopts:
515 for o in commands.globalopts:
528 c.append((o[0], o[1], options[o[1]], o[3]))
516 c.append((o[0], o[1], options[o[1]], o[3]))
529
517
530 try:
518 try:
531 args = fancyopts.fancyopts(args, c, cmdoptions, True)
519 args = fancyopts.fancyopts(args, c, cmdoptions, True)
532 except fancyopts.getopt.GetoptError, inst:
520 except fancyopts.getopt.GetoptError, inst:
533 raise error.CommandError(cmd, inst)
521 raise error.CommandError(cmd, inst)
534
522
535 # separate global options back out
523 # separate global options back out
536 for o in commands.globalopts:
524 for o in commands.globalopts:
537 n = o[1]
525 n = o[1]
538 options[n] = cmdoptions[n]
526 options[n] = cmdoptions[n]
539 del cmdoptions[n]
527 del cmdoptions[n]
540
528
541 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
529 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
542
530
543 def _parseconfig(ui, config):
531 def _parseconfig(ui, config):
544 """parse the --config options from the command line"""
532 """parse the --config options from the command line"""
545 configs = []
533 configs = []
546
534
547 for cfg in config:
535 for cfg in config:
548 try:
536 try:
549 name, value = cfg.split('=', 1)
537 name, value = cfg.split('=', 1)
550 section, name = name.split('.', 1)
538 section, name = name.split('.', 1)
551 if not section or not name:
539 if not section or not name:
552 raise IndexError
540 raise IndexError
553 ui.setconfig(section, name, value, '--config')
541 ui.setconfig(section, name, value, '--config')
554 configs.append((section, name, value))
542 configs.append((section, name, value))
555 except (IndexError, ValueError):
543 except (IndexError, ValueError):
556 raise util.Abort(_('malformed --config option: %r '
544 raise util.Abort(_('malformed --config option: %r '
557 '(use --config section.name=value)') % cfg)
545 '(use --config section.name=value)') % cfg)
558
546
559 return configs
547 return configs
560
548
561 def _earlygetopt(aliases, args):
549 def _earlygetopt(aliases, args):
562 """Return list of values for an option (or aliases).
550 """Return list of values for an option (or aliases).
563
551
564 The values are listed in the order they appear in args.
552 The values are listed in the order they appear in args.
565 The options and values are removed from args.
553 The options and values are removed from args.
566
554
567 >>> args = ['x', '--cwd', 'foo', 'y']
555 >>> args = ['x', '--cwd', 'foo', 'y']
568 >>> _earlygetopt(['--cwd'], args), args
556 >>> _earlygetopt(['--cwd'], args), args
569 (['foo'], ['x', 'y'])
557 (['foo'], ['x', 'y'])
570
558
571 >>> args = ['x', '--cwd=bar', 'y']
559 >>> args = ['x', '--cwd=bar', 'y']
572 >>> _earlygetopt(['--cwd'], args), args
560 >>> _earlygetopt(['--cwd'], args), args
573 (['bar'], ['x', 'y'])
561 (['bar'], ['x', 'y'])
574
562
575 >>> args = ['x', '-R', 'foo', 'y']
563 >>> args = ['x', '-R', 'foo', 'y']
576 >>> _earlygetopt(['-R'], args), args
564 >>> _earlygetopt(['-R'], args), args
577 (['foo'], ['x', 'y'])
565 (['foo'], ['x', 'y'])
578
566
579 >>> args = ['x', '-Rbar', 'y']
567 >>> args = ['x', '-Rbar', 'y']
580 >>> _earlygetopt(['-R'], args), args
568 >>> _earlygetopt(['-R'], args), args
581 (['bar'], ['x', 'y'])
569 (['bar'], ['x', 'y'])
582 """
570 """
583 try:
571 try:
584 argcount = args.index("--")
572 argcount = args.index("--")
585 except ValueError:
573 except ValueError:
586 argcount = len(args)
574 argcount = len(args)
587 shortopts = [opt for opt in aliases if len(opt) == 2]
575 shortopts = [opt for opt in aliases if len(opt) == 2]
588 values = []
576 values = []
589 pos = 0
577 pos = 0
590 while pos < argcount:
578 while pos < argcount:
591 fullarg = arg = args[pos]
579 fullarg = arg = args[pos]
592 equals = arg.find('=')
580 equals = arg.find('=')
593 if equals > -1:
581 if equals > -1:
594 arg = arg[:equals]
582 arg = arg[:equals]
595 if arg in aliases:
583 if arg in aliases:
596 del args[pos]
584 del args[pos]
597 if equals > -1:
585 if equals > -1:
598 values.append(fullarg[equals + 1:])
586 values.append(fullarg[equals + 1:])
599 argcount -= 1
587 argcount -= 1
600 else:
588 else:
601 if pos + 1 >= argcount:
589 if pos + 1 >= argcount:
602 # ignore and let getopt report an error if there is no value
590 # ignore and let getopt report an error if there is no value
603 break
591 break
604 values.append(args.pop(pos))
592 values.append(args.pop(pos))
605 argcount -= 2
593 argcount -= 2
606 elif arg[:2] in shortopts:
594 elif arg[:2] in shortopts:
607 # short option can have no following space, e.g. hg log -Rfoo
595 # short option can have no following space, e.g. hg log -Rfoo
608 values.append(args.pop(pos)[2:])
596 values.append(args.pop(pos)[2:])
609 argcount -= 1
597 argcount -= 1
610 else:
598 else:
611 pos += 1
599 pos += 1
612 return values
600 return values
613
601
614 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
602 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
615 # run pre-hook, and abort if it fails
603 # run pre-hook, and abort if it fails
616 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
604 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
617 pats=cmdpats, opts=cmdoptions)
605 pats=cmdpats, opts=cmdoptions)
618 ret = _runcommand(ui, options, cmd, d)
606 ret = _runcommand(ui, options, cmd, d)
619 # run post-hook, passing command result
607 # run post-hook, passing command result
620 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
608 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
621 result=ret, pats=cmdpats, opts=cmdoptions)
609 result=ret, pats=cmdpats, opts=cmdoptions)
622 return ret
610 return ret
623
611
624 def _getlocal(ui, rpath):
612 def _getlocal(ui, rpath):
625 """Return (path, local ui object) for the given target path.
613 """Return (path, local ui object) for the given target path.
626
614
627 Takes paths in [cwd]/.hg/hgrc into account."
615 Takes paths in [cwd]/.hg/hgrc into account."
628 """
616 """
629 try:
617 try:
630 wd = os.getcwd()
618 wd = os.getcwd()
631 except OSError, e:
619 except OSError, e:
632 raise util.Abort(_("error getting current working directory: %s") %
620 raise util.Abort(_("error getting current working directory: %s") %
633 e.strerror)
621 e.strerror)
634 path = cmdutil.findrepo(wd) or ""
622 path = cmdutil.findrepo(wd) or ""
635 if not path:
623 if not path:
636 lui = ui
624 lui = ui
637 else:
625 else:
638 lui = ui.copy()
626 lui = ui.copy()
639 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
627 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
640
628
641 if rpath and rpath[-1]:
629 if rpath and rpath[-1]:
642 path = lui.expandpath(rpath[-1])
630 path = lui.expandpath(rpath[-1])
643 lui = ui.copy()
631 lui = ui.copy()
644 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
632 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
645
633
646 return path, lui
634 return path, lui
647
635
648 def _checkshellalias(lui, ui, args):
636 def _checkshellalias(lui, ui, args):
649 options = {}
637 options = {}
650
638
651 try:
639 try:
652 args = fancyopts.fancyopts(args, commands.globalopts, options)
640 args = fancyopts.fancyopts(args, commands.globalopts, options)
653 except fancyopts.getopt.GetoptError:
641 except fancyopts.getopt.GetoptError:
654 return
642 return
655
643
656 if not args:
644 if not args:
657 return
645 return
658
646
659 norepo = commands.norepo
647 norepo = commands.norepo
660 optionalrepo = commands.optionalrepo
648 optionalrepo = commands.optionalrepo
661 def restorecommands():
649 def restorecommands():
662 commands.norepo = norepo
650 commands.norepo = norepo
663 commands.optionalrepo = optionalrepo
651 commands.optionalrepo = optionalrepo
664
652
665 cmdtable = commands.table.copy()
653 cmdtable = commands.table.copy()
666 addaliases(lui, cmdtable)
654 addaliases(lui, cmdtable)
667
655
668 cmd = args[0]
656 cmd = args[0]
669 try:
657 try:
670 aliases, entry = cmdutil.findcmd(cmd, cmdtable)
658 aliases, entry = cmdutil.findcmd(cmd, cmdtable)
671 except (error.AmbiguousCommand, error.UnknownCommand):
659 except (error.AmbiguousCommand, error.UnknownCommand):
672 restorecommands()
660 restorecommands()
673 return
661 return
674
662
675 cmd = aliases[0]
663 cmd = aliases[0]
676 fn = entry[0]
664 fn = entry[0]
677
665
678 if cmd and util.safehasattr(fn, 'shell'):
666 if cmd and util.safehasattr(fn, 'shell'):
679 d = lambda: fn(ui, *args[1:])
667 d = lambda: fn(ui, *args[1:])
680 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
668 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
681 [], {})
669 [], {})
682
670
683 restorecommands()
671 restorecommands()
684
672
685 _loaded = set()
673 _loaded = set()
686 def _dispatch(req):
674 def _dispatch(req):
687 args = req.args
675 args = req.args
688 ui = req.ui
676 ui = req.ui
689
677
690 # check for cwd
678 # check for cwd
691 cwd = _earlygetopt(['--cwd'], args)
679 cwd = _earlygetopt(['--cwd'], args)
692 if cwd:
680 if cwd:
693 os.chdir(cwd[-1])
681 os.chdir(cwd[-1])
694
682
695 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
683 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
696 path, lui = _getlocal(ui, rpath)
684 path, lui = _getlocal(ui, rpath)
697
685
698 # Now that we're operating in the right directory/repository with
686 # Now that we're operating in the right directory/repository with
699 # the right config settings, check for shell aliases
687 # the right config settings, check for shell aliases
700 shellaliasfn = _checkshellalias(lui, ui, args)
688 shellaliasfn = _checkshellalias(lui, ui, args)
701 if shellaliasfn:
689 if shellaliasfn:
702 return shellaliasfn()
690 return shellaliasfn()
703
691
704 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
692 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
705 # reposetup. Programs like TortoiseHg will call _dispatch several
693 # reposetup. Programs like TortoiseHg will call _dispatch several
706 # times so we keep track of configured extensions in _loaded.
694 # times so we keep track of configured extensions in _loaded.
707 extensions.loadall(lui)
695 extensions.loadall(lui)
708 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
696 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
709 # Propagate any changes to lui.__class__ by extensions
697 # Propagate any changes to lui.__class__ by extensions
710 ui.__class__ = lui.__class__
698 ui.__class__ = lui.__class__
711
699
712 # (uisetup and extsetup are handled in extensions.loadall)
700 # (uisetup and extsetup are handled in extensions.loadall)
713
701
714 for name, module in exts:
702 for name, module in exts:
715 cmdtable = getattr(module, 'cmdtable', {})
703 cmdtable = getattr(module, 'cmdtable', {})
716 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
704 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
717 if overrides:
705 if overrides:
718 ui.warn(_("extension '%s' overrides commands: %s\n")
706 ui.warn(_("extension '%s' overrides commands: %s\n")
719 % (name, " ".join(overrides)))
707 % (name, " ".join(overrides)))
720 commands.table.update(cmdtable)
708 commands.table.update(cmdtable)
721 _loaded.add(name)
709 _loaded.add(name)
722
710
723 # (reposetup is handled in hg.repository)
711 # (reposetup is handled in hg.repository)
724
712
725 addaliases(lui, commands.table)
713 addaliases(lui, commands.table)
726
714
727 # check for fallback encoding
715 # check for fallback encoding
728 fallback = lui.config('ui', 'fallbackencoding')
716 fallback = lui.config('ui', 'fallbackencoding')
729 if fallback:
717 if fallback:
730 encoding.fallbackencoding = fallback
718 encoding.fallbackencoding = fallback
731
719
732 fullargs = args
720 fullargs = args
733 cmd, func, args, options, cmdoptions = _parse(lui, args)
721 cmd, func, args, options, cmdoptions = _parse(lui, args)
734
722
735 if options["config"]:
723 if options["config"]:
736 raise util.Abort(_("option --config may not be abbreviated!"))
724 raise util.Abort(_("option --config may not be abbreviated!"))
737 if options["cwd"]:
725 if options["cwd"]:
738 raise util.Abort(_("option --cwd may not be abbreviated!"))
726 raise util.Abort(_("option --cwd may not be abbreviated!"))
739 if options["repository"]:
727 if options["repository"]:
740 raise util.Abort(_(
728 raise util.Abort(_(
741 "option -R has to be separated from other options (e.g. not -qR) "
729 "option -R has to be separated from other options (e.g. not -qR) "
742 "and --repository may only be abbreviated as --repo!"))
730 "and --repository may only be abbreviated as --repo!"))
743
731
744 if options["encoding"]:
732 if options["encoding"]:
745 encoding.encoding = options["encoding"]
733 encoding.encoding = options["encoding"]
746 if options["encodingmode"]:
734 if options["encodingmode"]:
747 encoding.encodingmode = options["encodingmode"]
735 encoding.encodingmode = options["encodingmode"]
748 if options["time"]:
736 if options["time"]:
749 def get_times():
737 def get_times():
750 t = os.times()
738 t = os.times()
751 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
739 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
752 t = (t[0], t[1], t[2], t[3], time.clock())
740 t = (t[0], t[1], t[2], t[3], time.clock())
753 return t
741 return t
754 s = get_times()
742 s = get_times()
755 def print_time():
743 def print_time():
756 t = get_times()
744 t = get_times()
757 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
745 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
758 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
746 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
759 atexit.register(print_time)
747 atexit.register(print_time)
760
748
761 uis = set([ui, lui])
749 uis = set([ui, lui])
762
750
763 if req.repo:
751 if req.repo:
764 uis.add(req.repo.ui)
752 uis.add(req.repo.ui)
765
753
766 if options['verbose'] or options['debug'] or options['quiet']:
754 if options['verbose'] or options['debug'] or options['quiet']:
767 for opt in ('verbose', 'debug', 'quiet'):
755 for opt in ('verbose', 'debug', 'quiet'):
768 val = str(bool(options[opt]))
756 val = str(bool(options[opt]))
769 for ui_ in uis:
757 for ui_ in uis:
770 ui_.setconfig('ui', opt, val, '--' + opt)
758 ui_.setconfig('ui', opt, val, '--' + opt)
771
759
772 if options['traceback']:
760 if options['traceback']:
773 for ui_ in uis:
761 for ui_ in uis:
774 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
762 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
775
763
776 if options['noninteractive']:
764 if options['noninteractive']:
777 for ui_ in uis:
765 for ui_ in uis:
778 ui_.setconfig('ui', 'interactive', 'off', '-y')
766 ui_.setconfig('ui', 'interactive', 'off', '-y')
779
767
780 if cmdoptions.get('insecure', False):
768 if cmdoptions.get('insecure', False):
781 for ui_ in uis:
769 for ui_ in uis:
782 ui_.setconfig('web', 'cacerts', '', '--insecure')
770 ui_.setconfig('web', 'cacerts', '', '--insecure')
783
771
784 if options['version']:
772 if options['version']:
785 return commands.version_(ui)
773 return commands.version_(ui)
786 if options['help']:
774 if options['help']:
787 return commands.help_(ui, cmd, command=True)
775 return commands.help_(ui, cmd, command=True)
788 elif not cmd:
776 elif not cmd:
789 return commands.help_(ui, 'shortlist')
777 return commands.help_(ui, 'shortlist')
790
778
791 repo = None
779 repo = None
792 cmdpats = args[:]
780 cmdpats = args[:]
793 if cmd not in commands.norepo.split():
781 if cmd not in commands.norepo.split():
794 # use the repo from the request only if we don't have -R
782 # use the repo from the request only if we don't have -R
795 if not rpath and not cwd:
783 if not rpath and not cwd:
796 repo = req.repo
784 repo = req.repo
797
785
798 if repo:
786 if repo:
799 # set the descriptors of the repo ui to those of ui
787 # set the descriptors of the repo ui to those of ui
800 repo.ui.fin = ui.fin
788 repo.ui.fin = ui.fin
801 repo.ui.fout = ui.fout
789 repo.ui.fout = ui.fout
802 repo.ui.ferr = ui.ferr
790 repo.ui.ferr = ui.ferr
803 else:
791 else:
804 try:
792 try:
805 repo = hg.repository(ui, path=path)
793 repo = hg.repository(ui, path=path)
806 if not repo.local():
794 if not repo.local():
807 raise util.Abort(_("repository '%s' is not local") % path)
795 raise util.Abort(_("repository '%s' is not local") % path)
808 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
796 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
809 except error.RequirementError:
797 except error.RequirementError:
810 raise
798 raise
811 except error.RepoError:
799 except error.RepoError:
812 if cmd not in commands.optionalrepo.split():
800 if cmd not in commands.optionalrepo.split():
813 if (cmd in commands.inferrepo.split() and
801 if (cmd in commands.inferrepo.split() and
814 args and not path): # try to infer -R from command args
802 args and not path): # try to infer -R from command args
815 repos = map(cmdutil.findrepo, args)
803 repos = map(cmdutil.findrepo, args)
816 guess = repos[0]
804 guess = repos[0]
817 if guess and repos.count(guess) == len(repos):
805 if guess and repos.count(guess) == len(repos):
818 req.args = ['--repository', guess] + fullargs
806 req.args = ['--repository', guess] + fullargs
819 return _dispatch(req)
807 return _dispatch(req)
820 if not path:
808 if not path:
821 raise error.RepoError(_("no repository found in '%s'"
809 raise error.RepoError(_("no repository found in '%s'"
822 " (.hg not found)")
810 " (.hg not found)")
823 % os.getcwd())
811 % os.getcwd())
824 raise
812 raise
825 if repo:
813 if repo:
826 ui = repo.ui
814 ui = repo.ui
827 if options['hidden']:
815 if options['hidden']:
828 repo = repo.unfiltered()
816 repo = repo.unfiltered()
829 args.insert(0, repo)
817 args.insert(0, repo)
830 elif rpath:
818 elif rpath:
831 ui.warn(_("warning: --repository ignored\n"))
819 ui.warn(_("warning: --repository ignored\n"))
832
820
833 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
821 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
834 ui.log("command", '%s\n', msg)
822 ui.log("command", '%s\n', msg)
835 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
823 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
836 try:
824 try:
837 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
825 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
838 cmdpats, cmdoptions)
826 cmdpats, cmdoptions)
839 finally:
827 finally:
840 if repo and repo != req.repo:
828 if repo and repo != req.repo:
841 repo.close()
829 repo.close()
842
830
843 def lsprofile(ui, func, fp):
831 def lsprofile(ui, func, fp):
844 format = ui.config('profiling', 'format', default='text')
832 format = ui.config('profiling', 'format', default='text')
845 field = ui.config('profiling', 'sort', default='inlinetime')
833 field = ui.config('profiling', 'sort', default='inlinetime')
846 limit = ui.configint('profiling', 'limit', default=30)
834 limit = ui.configint('profiling', 'limit', default=30)
847 climit = ui.configint('profiling', 'nested', default=5)
835 climit = ui.configint('profiling', 'nested', default=5)
848
836
849 if format not in ['text', 'kcachegrind']:
837 if format not in ['text', 'kcachegrind']:
850 ui.warn(_("unrecognized profiling format '%s'"
838 ui.warn(_("unrecognized profiling format '%s'"
851 " - Ignored\n") % format)
839 " - Ignored\n") % format)
852 format = 'text'
840 format = 'text'
853
841
854 try:
842 try:
855 from mercurial import lsprof
843 from mercurial import lsprof
856 except ImportError:
844 except ImportError:
857 raise util.Abort(_(
845 raise util.Abort(_(
858 'lsprof not available - install from '
846 'lsprof not available - install from '
859 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
847 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
860 p = lsprof.Profiler()
848 p = lsprof.Profiler()
861 p.enable(subcalls=True)
849 p.enable(subcalls=True)
862 try:
850 try:
863 return func()
851 return func()
864 finally:
852 finally:
865 p.disable()
853 p.disable()
866
854
867 if format == 'kcachegrind':
855 if format == 'kcachegrind':
868 import lsprofcalltree
856 import lsprofcalltree
869 calltree = lsprofcalltree.KCacheGrind(p)
857 calltree = lsprofcalltree.KCacheGrind(p)
870 calltree.output(fp)
858 calltree.output(fp)
871 else:
859 else:
872 # format == 'text'
860 # format == 'text'
873 stats = lsprof.Stats(p.getstats())
861 stats = lsprof.Stats(p.getstats())
874 stats.sort(field)
862 stats.sort(field)
875 stats.pprint(limit=limit, file=fp, climit=climit)
863 stats.pprint(limit=limit, file=fp, climit=climit)
876
864
877 def statprofile(ui, func, fp):
865 def statprofile(ui, func, fp):
878 try:
866 try:
879 import statprof
867 import statprof
880 except ImportError:
868 except ImportError:
881 raise util.Abort(_(
869 raise util.Abort(_(
882 'statprof not available - install using "easy_install statprof"'))
870 'statprof not available - install using "easy_install statprof"'))
883
871
884 freq = ui.configint('profiling', 'freq', default=1000)
872 freq = ui.configint('profiling', 'freq', default=1000)
885 if freq > 0:
873 if freq > 0:
886 statprof.reset(freq)
874 statprof.reset(freq)
887 else:
875 else:
888 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
876 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
889
877
890 statprof.start()
878 statprof.start()
891 try:
879 try:
892 return func()
880 return func()
893 finally:
881 finally:
894 statprof.stop()
882 statprof.stop()
895 statprof.display(fp)
883 statprof.display(fp)
896
884
897 def _runcommand(ui, options, cmd, cmdfunc):
885 def _runcommand(ui, options, cmd, cmdfunc):
898 def checkargs():
886 def checkargs():
899 try:
887 try:
900 return cmdfunc()
888 return cmdfunc()
901 except error.SignatureError:
889 except error.SignatureError:
902 raise error.CommandError(cmd, _("invalid arguments"))
890 raise error.CommandError(cmd, _("invalid arguments"))
903
891
904 if options['profile']:
892 if options['profile']:
905 profiler = os.getenv('HGPROF')
893 profiler = os.getenv('HGPROF')
906 if profiler is None:
894 if profiler is None:
907 profiler = ui.config('profiling', 'type', default='ls')
895 profiler = ui.config('profiling', 'type', default='ls')
908 if profiler not in ('ls', 'stat'):
896 if profiler not in ('ls', 'stat'):
909 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
897 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
910 profiler = 'ls'
898 profiler = 'ls'
911
899
912 output = ui.config('profiling', 'output')
900 output = ui.config('profiling', 'output')
913
901
914 if output:
902 if output:
915 path = ui.expandpath(output)
903 path = ui.expandpath(output)
916 fp = open(path, 'wb')
904 fp = open(path, 'wb')
917 else:
905 else:
918 fp = sys.stderr
906 fp = sys.stderr
919
907
920 try:
908 try:
921 if profiler == 'ls':
909 if profiler == 'ls':
922 return lsprofile(ui, checkargs, fp)
910 return lsprofile(ui, checkargs, fp)
923 else:
911 else:
924 return statprofile(ui, checkargs, fp)
912 return statprofile(ui, checkargs, fp)
925 finally:
913 finally:
926 if output:
914 if output:
927 fp.close()
915 fp.close()
928 else:
916 else:
929 return checkargs()
917 return checkargs()
@@ -1,508 +1,508 b''
1 # help.py - help data for mercurial
1 # help.py - help data for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 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 gettext, _
8 from i18n import gettext, _
9 import itertools, sys, os
9 import itertools, sys, os
10 import error
10 import error
11 import extensions, revset, fileset, templatekw, templatefilters, filemerge
11 import extensions, revset, fileset, templatekw, templatefilters, filemerge
12 import encoding, util, minirst
12 import encoding, util, minirst
13 import cmdutil
13 import cmdutil
14
14
15 def listexts(header, exts, indent=1, showdeprecated=False):
15 def listexts(header, exts, indent=1, showdeprecated=False):
16 '''return a text listing of the given extensions'''
16 '''return a text listing of the given extensions'''
17 rst = []
17 rst = []
18 if exts:
18 if exts:
19 rst.append('\n%s\n\n' % header)
19 rst.append('\n%s\n\n' % header)
20 for name, desc in sorted(exts.iteritems()):
20 for name, desc in sorted(exts.iteritems()):
21 if '(DEPRECATED)' in desc and not showdeprecated:
21 if '(DEPRECATED)' in desc and not showdeprecated:
22 continue
22 continue
23 rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
23 rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
24 return rst
24 return rst
25
25
26 def extshelp():
26 def extshelp():
27 rst = loaddoc('extensions')().splitlines(True)
27 rst = loaddoc('extensions')().splitlines(True)
28 rst.extend(listexts(
28 rst.extend(listexts(
29 _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
29 _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
30 rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
30 rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
31 doc = ''.join(rst)
31 doc = ''.join(rst)
32 return doc
32 return doc
33
33
34 def optrst(header, options, verbose):
34 def optrst(header, options, verbose):
35 data = []
35 data = []
36 multioccur = False
36 multioccur = False
37 for option in options:
37 for option in options:
38 if len(option) == 5:
38 if len(option) == 5:
39 shortopt, longopt, default, desc, optlabel = option
39 shortopt, longopt, default, desc, optlabel = option
40 else:
40 else:
41 shortopt, longopt, default, desc = option
41 shortopt, longopt, default, desc = option
42 optlabel = _("VALUE") # default label
42 optlabel = _("VALUE") # default label
43
43
44 if not verbose and ("DEPRECATED" in desc or _("DEPRECATED") in desc):
44 if not verbose and ("DEPRECATED" in desc or _("DEPRECATED") in desc):
45 continue
45 continue
46
46
47 so = ''
47 so = ''
48 if shortopt:
48 if shortopt:
49 so = '-' + shortopt
49 so = '-' + shortopt
50 lo = '--' + longopt
50 lo = '--' + longopt
51 if default:
51 if default:
52 desc += _(" (default: %s)") % default
52 desc += _(" (default: %s)") % default
53
53
54 if isinstance(default, list):
54 if isinstance(default, list):
55 lo += " %s [+]" % optlabel
55 lo += " %s [+]" % optlabel
56 multioccur = True
56 multioccur = True
57 elif (default is not None) and not isinstance(default, bool):
57 elif (default is not None) and not isinstance(default, bool):
58 lo += " %s" % optlabel
58 lo += " %s" % optlabel
59
59
60 data.append((so, lo, desc))
60 data.append((so, lo, desc))
61
61
62 if multioccur:
62 if multioccur:
63 header += (_(" ([+] can be repeated)"))
63 header += (_(" ([+] can be repeated)"))
64
64
65 rst = ['\n%s:\n\n' % header]
65 rst = ['\n%s:\n\n' % header]
66 rst.extend(minirst.maketable(data, 1))
66 rst.extend(minirst.maketable(data, 1))
67
67
68 return ''.join(rst)
68 return ''.join(rst)
69
69
70 def indicateomitted(rst, omitted, notomitted=None):
70 def indicateomitted(rst, omitted, notomitted=None):
71 rst.append('\n\n.. container:: omitted\n\n %s\n\n' % omitted)
71 rst.append('\n\n.. container:: omitted\n\n %s\n\n' % omitted)
72 if notomitted:
72 if notomitted:
73 rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
73 rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
74
74
75 def topicmatch(kw):
75 def topicmatch(kw):
76 """Return help topics matching kw.
76 """Return help topics matching kw.
77
77
78 Returns {'section': [(name, summary), ...], ...} where section is
78 Returns {'section': [(name, summary), ...], ...} where section is
79 one of topics, commands, extensions, or extensioncommands.
79 one of topics, commands, extensions, or extensioncommands.
80 """
80 """
81 kw = encoding.lower(kw)
81 kw = encoding.lower(kw)
82 def lowercontains(container):
82 def lowercontains(container):
83 return kw in encoding.lower(container) # translated in helptable
83 return kw in encoding.lower(container) # translated in helptable
84 results = {'topics': [],
84 results = {'topics': [],
85 'commands': [],
85 'commands': [],
86 'extensions': [],
86 'extensions': [],
87 'extensioncommands': [],
87 'extensioncommands': [],
88 }
88 }
89 for names, header, doc in helptable:
89 for names, header, doc in helptable:
90 if (sum(map(lowercontains, names))
90 if (sum(map(lowercontains, names))
91 or lowercontains(header)
91 or lowercontains(header)
92 or lowercontains(doc())):
92 or lowercontains(doc())):
93 results['topics'].append((names[0], header))
93 results['topics'].append((names[0], header))
94 import commands # avoid cycle
94 import commands # avoid cycle
95 for cmd, entry in commands.table.iteritems():
95 for cmd, entry in commands.table.iteritems():
96 if len(entry) == 3:
96 if len(entry) == 3:
97 summary = entry[2]
97 summary = entry[2]
98 else:
98 else:
99 summary = ''
99 summary = ''
100 # translate docs *before* searching there
100 # translate docs *before* searching there
101 docs = _(getattr(entry[0], '__doc__', None)) or ''
101 docs = _(getattr(entry[0], '__doc__', None)) or ''
102 if kw in cmd or lowercontains(summary) or lowercontains(docs):
102 if kw in cmd or lowercontains(summary) or lowercontains(docs):
103 doclines = docs.splitlines()
103 doclines = docs.splitlines()
104 if doclines:
104 if doclines:
105 summary = doclines[0]
105 summary = doclines[0]
106 cmdname = cmd.split('|')[0].lstrip('^')
106 cmdname = cmd.split('|')[0].lstrip('^')
107 results['commands'].append((cmdname, summary))
107 results['commands'].append((cmdname, summary))
108 for name, docs in itertools.chain(
108 for name, docs in itertools.chain(
109 extensions.enabled(False).iteritems(),
109 extensions.enabled(False).iteritems(),
110 extensions.disabled().iteritems()):
110 extensions.disabled().iteritems()):
111 # extensions.load ignores the UI argument
111 # extensions.load ignores the UI argument
112 mod = extensions.load(None, name, '')
112 mod = extensions.load(None, name, '')
113 name = name.split('.')[-1]
113 name = name.split('.')[-1]
114 if lowercontains(name) or lowercontains(docs):
114 if lowercontains(name) or lowercontains(docs):
115 # extension docs are already translated
115 # extension docs are already translated
116 results['extensions'].append((name, docs.splitlines()[0]))
116 results['extensions'].append((name, docs.splitlines()[0]))
117 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
117 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
118 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
118 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
119 cmdname = cmd.split('|')[0].lstrip('^')
119 cmdname = cmd.split('|')[0].lstrip('^')
120 if entry[0].__doc__:
120 if entry[0].__doc__:
121 cmddoc = gettext(entry[0].__doc__).splitlines()[0]
121 cmddoc = gettext(entry[0].__doc__).splitlines()[0]
122 else:
122 else:
123 cmddoc = _('(no help text available)')
123 cmddoc = _('(no help text available)')
124 results['extensioncommands'].append((cmdname, cmddoc))
124 results['extensioncommands'].append((cmdname, cmddoc))
125 return results
125 return results
126
126
127 def loaddoc(topic):
127 def loaddoc(topic):
128 """Return a delayed loader for help/topic.txt."""
128 """Return a delayed loader for help/topic.txt."""
129
129
130 def loader():
130 def loader():
131 if util.mainfrozen():
131 if util.mainfrozen():
132 module = sys.executable
132 module = sys.executable
133 else:
133 else:
134 module = __file__
134 module = __file__
135 base = os.path.dirname(module)
135 base = os.path.dirname(module)
136
136
137 for dir in ('.', '..'):
137 for dir in ('.', '..'):
138 docdir = os.path.join(base, dir, 'help')
138 docdir = os.path.join(base, dir, 'help')
139 if os.path.isdir(docdir):
139 if os.path.isdir(docdir):
140 break
140 break
141
141
142 path = os.path.join(docdir, topic + ".txt")
142 path = os.path.join(docdir, topic + ".txt")
143 doc = gettext(util.readfile(path))
143 doc = gettext(util.readfile(path))
144 for rewriter in helphooks.get(topic, []):
144 for rewriter in helphooks.get(topic, []):
145 doc = rewriter(topic, doc)
145 doc = rewriter(topic, doc)
146 return doc
146 return doc
147
147
148 return loader
148 return loader
149
149
150 helptable = sorted([
150 helptable = sorted([
151 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
151 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
152 (["dates"], _("Date Formats"), loaddoc('dates')),
152 (["dates"], _("Date Formats"), loaddoc('dates')),
153 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
153 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
154 (['environment', 'env'], _('Environment Variables'),
154 (['environment', 'env'], _('Environment Variables'),
155 loaddoc('environment')),
155 loaddoc('environment')),
156 (['revisions', 'revs'], _('Specifying Single Revisions'),
156 (['revisions', 'revs'], _('Specifying Single Revisions'),
157 loaddoc('revisions')),
157 loaddoc('revisions')),
158 (['multirevs', 'mrevs'], _('Specifying Multiple Revisions'),
158 (['multirevs', 'mrevs'], _('Specifying Multiple Revisions'),
159 loaddoc('multirevs')),
159 loaddoc('multirevs')),
160 (['revsets', 'revset'], _("Specifying Revision Sets"), loaddoc('revsets')),
160 (['revsets', 'revset'], _("Specifying Revision Sets"), loaddoc('revsets')),
161 (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets')),
161 (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets')),
162 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
162 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
163 (['merge-tools', 'mergetools'], _('Merge Tools'), loaddoc('merge-tools')),
163 (['merge-tools', 'mergetools'], _('Merge Tools'), loaddoc('merge-tools')),
164 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
164 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
165 loaddoc('templates')),
165 loaddoc('templates')),
166 (['urls'], _('URL Paths'), loaddoc('urls')),
166 (['urls'], _('URL Paths'), loaddoc('urls')),
167 (["extensions"], _("Using Additional Features"), extshelp),
167 (["extensions"], _("Using Additional Features"), extshelp),
168 (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos')),
168 (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos')),
169 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
169 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
170 (["glossary"], _("Glossary"), loaddoc('glossary')),
170 (["glossary"], _("Glossary"), loaddoc('glossary')),
171 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
171 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
172 loaddoc('hgignore')),
172 loaddoc('hgignore')),
173 (["phases"], _("Working with Phases"), loaddoc('phases')),
173 (["phases"], _("Working with Phases"), loaddoc('phases')),
174 ])
174 ])
175
175
176 # Map topics to lists of callable taking the current topic help and
176 # Map topics to lists of callable taking the current topic help and
177 # returning the updated version
177 # returning the updated version
178 helphooks = {}
178 helphooks = {}
179
179
180 def addtopichook(topic, rewriter):
180 def addtopichook(topic, rewriter):
181 helphooks.setdefault(topic, []).append(rewriter)
181 helphooks.setdefault(topic, []).append(rewriter)
182
182
183 def makeitemsdoc(topic, doc, marker, items):
183 def makeitemsdoc(topic, doc, marker, items):
184 """Extract docstring from the items key to function mapping, build a
184 """Extract docstring from the items key to function mapping, build a
185 .single documentation block and use it to overwrite the marker in doc
185 .single documentation block and use it to overwrite the marker in doc
186 """
186 """
187 entries = []
187 entries = []
188 for name in sorted(items):
188 for name in sorted(items):
189 text = (items[name].__doc__ or '').rstrip()
189 text = (items[name].__doc__ or '').rstrip()
190 if not text:
190 if not text:
191 continue
191 continue
192 text = gettext(text)
192 text = gettext(text)
193 lines = text.splitlines()
193 lines = text.splitlines()
194 doclines = [(lines[0])]
194 doclines = [(lines[0])]
195 for l in lines[1:]:
195 for l in lines[1:]:
196 # Stop once we find some Python doctest
196 # Stop once we find some Python doctest
197 if l.strip().startswith('>>>'):
197 if l.strip().startswith('>>>'):
198 break
198 break
199 doclines.append(' ' + l.strip())
199 doclines.append(' ' + l.strip())
200 entries.append('\n'.join(doclines))
200 entries.append('\n'.join(doclines))
201 entries = '\n\n'.join(entries)
201 entries = '\n\n'.join(entries)
202 return doc.replace(marker, entries)
202 return doc.replace(marker, entries)
203
203
204 def addtopicsymbols(topic, marker, symbols):
204 def addtopicsymbols(topic, marker, symbols):
205 def add(topic, doc):
205 def add(topic, doc):
206 return makeitemsdoc(topic, doc, marker, symbols)
206 return makeitemsdoc(topic, doc, marker, symbols)
207 addtopichook(topic, add)
207 addtopichook(topic, add)
208
208
209 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
209 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
210 addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
210 addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
211 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
211 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
212 addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords)
212 addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords)
213 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
213 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
214
214
215 def help_(ui, name, unknowncmd=False, full=True, **opts):
215 def help_(ui, name, unknowncmd=False, full=True, **opts):
216 '''
216 '''
217 Generate the help for 'name' as unformatted restructured text. If
217 Generate the help for 'name' as unformatted restructured text. If
218 'name' is None, describe the commands available.
218 'name' is None, describe the commands available.
219 '''
219 '''
220
220
221 import commands # avoid cycle
221 import commands # avoid cycle
222
222
223 def helpcmd(name):
223 def helpcmd(name):
224 try:
224 try:
225 aliases, entry = cmdutil.findcmd(name, commands.table,
225 aliases, entry = cmdutil.findcmd(name, commands.table,
226 strict=unknowncmd)
226 strict=unknowncmd)
227 except error.AmbiguousCommand, inst:
227 except error.AmbiguousCommand, inst:
228 # py3k fix: except vars can't be used outside the scope of the
228 # py3k fix: except vars can't be used outside the scope of the
229 # except block, nor can be used inside a lambda. python issue4617
229 # except block, nor can be used inside a lambda. python issue4617
230 prefix = inst.args[0]
230 prefix = inst.args[0]
231 select = lambda c: c.lstrip('^').startswith(prefix)
231 select = lambda c: c.lstrip('^').startswith(prefix)
232 rst = helplist(select)
232 rst = helplist(select)
233 return rst
233 return rst
234
234
235 rst = []
235 rst = []
236
236
237 # check if it's an invalid alias and display its error if it is
237 # check if it's an invalid alias and display its error if it is
238 if getattr(entry[0], 'badalias', False):
238 if getattr(entry[0], 'badalias', None):
239 if not unknowncmd:
239 if not unknowncmd:
240 ui.pushbuffer()
240 ui.pushbuffer()
241 entry[0](ui)
241 entry[0](ui)
242 rst.append(ui.popbuffer())
242 rst.append(ui.popbuffer())
243 return rst
243 return rst
244
244
245 # synopsis
245 # synopsis
246 if len(entry) > 2:
246 if len(entry) > 2:
247 if entry[2].startswith('hg'):
247 if entry[2].startswith('hg'):
248 rst.append("%s\n" % entry[2])
248 rst.append("%s\n" % entry[2])
249 else:
249 else:
250 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
250 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
251 else:
251 else:
252 rst.append('hg %s\n' % aliases[0])
252 rst.append('hg %s\n' % aliases[0])
253 # aliases
253 # aliases
254 if full and not ui.quiet and len(aliases) > 1:
254 if full and not ui.quiet and len(aliases) > 1:
255 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
255 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
256 rst.append('\n')
256 rst.append('\n')
257
257
258 # description
258 # description
259 doc = gettext(entry[0].__doc__)
259 doc = gettext(entry[0].__doc__)
260 if not doc:
260 if not doc:
261 doc = _("(no help text available)")
261 doc = _("(no help text available)")
262 if util.safehasattr(entry[0], 'definition'): # aliased command
262 if util.safehasattr(entry[0], 'definition'): # aliased command
263 if entry[0].definition.startswith('!'): # shell alias
263 if entry[0].definition.startswith('!'): # shell alias
264 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
264 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
265 else:
265 else:
266 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
266 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
267 doc = doc.splitlines(True)
267 doc = doc.splitlines(True)
268 if ui.quiet or not full:
268 if ui.quiet or not full:
269 rst.append(doc[0])
269 rst.append(doc[0])
270 else:
270 else:
271 rst.extend(doc)
271 rst.extend(doc)
272 rst.append('\n')
272 rst.append('\n')
273
273
274 # check if this command shadows a non-trivial (multi-line)
274 # check if this command shadows a non-trivial (multi-line)
275 # extension help text
275 # extension help text
276 try:
276 try:
277 mod = extensions.find(name)
277 mod = extensions.find(name)
278 doc = gettext(mod.__doc__) or ''
278 doc = gettext(mod.__doc__) or ''
279 if '\n' in doc.strip():
279 if '\n' in doc.strip():
280 msg = _('(use "hg help -e %s" to show help for '
280 msg = _('(use "hg help -e %s" to show help for '
281 'the %s extension)') % (name, name)
281 'the %s extension)') % (name, name)
282 rst.append('\n%s\n' % msg)
282 rst.append('\n%s\n' % msg)
283 except KeyError:
283 except KeyError:
284 pass
284 pass
285
285
286 # options
286 # options
287 if not ui.quiet and entry[1]:
287 if not ui.quiet and entry[1]:
288 rst.append(optrst(_("options"), entry[1], ui.verbose))
288 rst.append(optrst(_("options"), entry[1], ui.verbose))
289
289
290 if ui.verbose:
290 if ui.verbose:
291 rst.append(optrst(_("global options"),
291 rst.append(optrst(_("global options"),
292 commands.globalopts, ui.verbose))
292 commands.globalopts, ui.verbose))
293
293
294 if not ui.verbose:
294 if not ui.verbose:
295 if not full:
295 if not full:
296 rst.append(_('\n(use "hg %s -h" to show more help)\n')
296 rst.append(_('\n(use "hg %s -h" to show more help)\n')
297 % name)
297 % name)
298 elif not ui.quiet:
298 elif not ui.quiet:
299 rst.append(_('\n(some details hidden, use --verbose '
299 rst.append(_('\n(some details hidden, use --verbose '
300 'to show complete help)'))
300 'to show complete help)'))
301
301
302 return rst
302 return rst
303
303
304
304
305 def helplist(select=None):
305 def helplist(select=None):
306 # list of commands
306 # list of commands
307 if name == "shortlist":
307 if name == "shortlist":
308 header = _('basic commands:\n\n')
308 header = _('basic commands:\n\n')
309 elif name == "debug":
309 elif name == "debug":
310 header = _('debug commands (internal and unsupported):\n\n')
310 header = _('debug commands (internal and unsupported):\n\n')
311 else:
311 else:
312 header = _('list of commands:\n\n')
312 header = _('list of commands:\n\n')
313
313
314 h = {}
314 h = {}
315 cmds = {}
315 cmds = {}
316 for c, e in commands.table.iteritems():
316 for c, e in commands.table.iteritems():
317 f = c.split("|", 1)[0]
317 f = c.split("|", 1)[0]
318 if select and not select(f):
318 if select and not select(f):
319 continue
319 continue
320 if (not select and name != 'shortlist' and
320 if (not select and name != 'shortlist' and
321 e[0].__module__ != commands.__name__):
321 e[0].__module__ != commands.__name__):
322 continue
322 continue
323 if name == "shortlist" and not f.startswith("^"):
323 if name == "shortlist" and not f.startswith("^"):
324 continue
324 continue
325 f = f.lstrip("^")
325 f = f.lstrip("^")
326 if not ui.debugflag and f.startswith("debug") and name != "debug":
326 if not ui.debugflag and f.startswith("debug") and name != "debug":
327 continue
327 continue
328 doc = e[0].__doc__
328 doc = e[0].__doc__
329 if doc and 'DEPRECATED' in doc and not ui.verbose:
329 if doc and 'DEPRECATED' in doc and not ui.verbose:
330 continue
330 continue
331 doc = gettext(doc)
331 doc = gettext(doc)
332 if not doc:
332 if not doc:
333 doc = _("(no help text available)")
333 doc = _("(no help text available)")
334 h[f] = doc.splitlines()[0].rstrip()
334 h[f] = doc.splitlines()[0].rstrip()
335 cmds[f] = c.lstrip("^")
335 cmds[f] = c.lstrip("^")
336
336
337 rst = []
337 rst = []
338 if not h:
338 if not h:
339 if not ui.quiet:
339 if not ui.quiet:
340 rst.append(_('no commands defined\n'))
340 rst.append(_('no commands defined\n'))
341 return rst
341 return rst
342
342
343 if not ui.quiet:
343 if not ui.quiet:
344 rst.append(header)
344 rst.append(header)
345 fns = sorted(h)
345 fns = sorted(h)
346 for f in fns:
346 for f in fns:
347 if ui.verbose:
347 if ui.verbose:
348 commacmds = cmds[f].replace("|",", ")
348 commacmds = cmds[f].replace("|",", ")
349 rst.append(" :%s: %s\n" % (commacmds, h[f]))
349 rst.append(" :%s: %s\n" % (commacmds, h[f]))
350 else:
350 else:
351 rst.append(' :%s: %s\n' % (f, h[f]))
351 rst.append(' :%s: %s\n' % (f, h[f]))
352
352
353 if not name:
353 if not name:
354 exts = listexts(_('enabled extensions:'), extensions.enabled())
354 exts = listexts(_('enabled extensions:'), extensions.enabled())
355 if exts:
355 if exts:
356 rst.append('\n')
356 rst.append('\n')
357 rst.extend(exts)
357 rst.extend(exts)
358
358
359 rst.append(_("\nadditional help topics:\n\n"))
359 rst.append(_("\nadditional help topics:\n\n"))
360 topics = []
360 topics = []
361 for names, header, doc in helptable:
361 for names, header, doc in helptable:
362 topics.append((names[0], header))
362 topics.append((names[0], header))
363 for t, desc in topics:
363 for t, desc in topics:
364 rst.append(" :%s: %s\n" % (t, desc))
364 rst.append(" :%s: %s\n" % (t, desc))
365
365
366 if ui.quiet:
366 if ui.quiet:
367 pass
367 pass
368 elif ui.verbose:
368 elif ui.verbose:
369 rst.append('\n%s\n' % optrst(_("global options"),
369 rst.append('\n%s\n' % optrst(_("global options"),
370 commands.globalopts, ui.verbose))
370 commands.globalopts, ui.verbose))
371 if name == 'shortlist':
371 if name == 'shortlist':
372 rst.append(_('\n(use "hg help" for the full list '
372 rst.append(_('\n(use "hg help" for the full list '
373 'of commands)\n'))
373 'of commands)\n'))
374 else:
374 else:
375 if name == 'shortlist':
375 if name == 'shortlist':
376 rst.append(_('\n(use "hg help" for the full list of commands '
376 rst.append(_('\n(use "hg help" for the full list of commands '
377 'or "hg -v" for details)\n'))
377 'or "hg -v" for details)\n'))
378 elif name and not full:
378 elif name and not full:
379 rst.append(_('\n(use "hg help %s" to show the full help '
379 rst.append(_('\n(use "hg help %s" to show the full help '
380 'text)\n') % name)
380 'text)\n') % name)
381 else:
381 else:
382 rst.append(_('\n(use "hg help -v%s" to show built-in aliases '
382 rst.append(_('\n(use "hg help -v%s" to show built-in aliases '
383 'and global options)\n')
383 'and global options)\n')
384 % (name and " " + name or ""))
384 % (name and " " + name or ""))
385 return rst
385 return rst
386
386
387 def helptopic(name):
387 def helptopic(name):
388 for names, header, doc in helptable:
388 for names, header, doc in helptable:
389 if name in names:
389 if name in names:
390 break
390 break
391 else:
391 else:
392 raise error.UnknownCommand(name)
392 raise error.UnknownCommand(name)
393
393
394 rst = [minirst.section(header)]
394 rst = [minirst.section(header)]
395
395
396 # description
396 # description
397 if not doc:
397 if not doc:
398 rst.append(" %s\n" % _("(no help text available)"))
398 rst.append(" %s\n" % _("(no help text available)"))
399 if callable(doc):
399 if callable(doc):
400 rst += [" %s\n" % l for l in doc().splitlines()]
400 rst += [" %s\n" % l for l in doc().splitlines()]
401
401
402 if not ui.verbose:
402 if not ui.verbose:
403 omitted = _('(some details hidden, use --verbose'
403 omitted = _('(some details hidden, use --verbose'
404 ' to show complete help)')
404 ' to show complete help)')
405 indicateomitted(rst, omitted)
405 indicateomitted(rst, omitted)
406
406
407 try:
407 try:
408 cmdutil.findcmd(name, commands.table)
408 cmdutil.findcmd(name, commands.table)
409 rst.append(_('\nuse "hg help -c %s" to see help for '
409 rst.append(_('\nuse "hg help -c %s" to see help for '
410 'the %s command\n') % (name, name))
410 'the %s command\n') % (name, name))
411 except error.UnknownCommand:
411 except error.UnknownCommand:
412 pass
412 pass
413 return rst
413 return rst
414
414
415 def helpext(name):
415 def helpext(name):
416 try:
416 try:
417 mod = extensions.find(name)
417 mod = extensions.find(name)
418 doc = gettext(mod.__doc__) or _('no help text available')
418 doc = gettext(mod.__doc__) or _('no help text available')
419 except KeyError:
419 except KeyError:
420 mod = None
420 mod = None
421 doc = extensions.disabledext(name)
421 doc = extensions.disabledext(name)
422 if not doc:
422 if not doc:
423 raise error.UnknownCommand(name)
423 raise error.UnknownCommand(name)
424
424
425 if '\n' not in doc:
425 if '\n' not in doc:
426 head, tail = doc, ""
426 head, tail = doc, ""
427 else:
427 else:
428 head, tail = doc.split('\n', 1)
428 head, tail = doc.split('\n', 1)
429 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
429 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
430 if tail:
430 if tail:
431 rst.extend(tail.splitlines(True))
431 rst.extend(tail.splitlines(True))
432 rst.append('\n')
432 rst.append('\n')
433
433
434 if not ui.verbose:
434 if not ui.verbose:
435 omitted = _('(some details hidden, use --verbose'
435 omitted = _('(some details hidden, use --verbose'
436 ' to show complete help)')
436 ' to show complete help)')
437 indicateomitted(rst, omitted)
437 indicateomitted(rst, omitted)
438
438
439 if mod:
439 if mod:
440 try:
440 try:
441 ct = mod.cmdtable
441 ct = mod.cmdtable
442 except AttributeError:
442 except AttributeError:
443 ct = {}
443 ct = {}
444 modcmds = set([c.split('|', 1)[0] for c in ct])
444 modcmds = set([c.split('|', 1)[0] for c in ct])
445 rst.extend(helplist(modcmds.__contains__))
445 rst.extend(helplist(modcmds.__contains__))
446 else:
446 else:
447 rst.append(_('(use "hg help extensions" for information on enabling'
447 rst.append(_('(use "hg help extensions" for information on enabling'
448 ' extensions)\n'))
448 ' extensions)\n'))
449 return rst
449 return rst
450
450
451 def helpextcmd(name):
451 def helpextcmd(name):
452 cmd, ext, mod = extensions.disabledcmd(ui, name,
452 cmd, ext, mod = extensions.disabledcmd(ui, name,
453 ui.configbool('ui', 'strict'))
453 ui.configbool('ui', 'strict'))
454 doc = gettext(mod.__doc__).splitlines()[0]
454 doc = gettext(mod.__doc__).splitlines()[0]
455
455
456 rst = listexts(_("'%s' is provided by the following "
456 rst = listexts(_("'%s' is provided by the following "
457 "extension:") % cmd, {ext: doc}, indent=4)
457 "extension:") % cmd, {ext: doc}, indent=4)
458 rst.append('\n')
458 rst.append('\n')
459 rst.append(_('(use "hg help extensions" for information on enabling '
459 rst.append(_('(use "hg help extensions" for information on enabling '
460 'extensions)\n'))
460 'extensions)\n'))
461 return rst
461 return rst
462
462
463
463
464 rst = []
464 rst = []
465 kw = opts.get('keyword')
465 kw = opts.get('keyword')
466 if kw:
466 if kw:
467 matches = topicmatch(kw)
467 matches = topicmatch(kw)
468 for t, title in (('topics', _('Topics')),
468 for t, title in (('topics', _('Topics')),
469 ('commands', _('Commands')),
469 ('commands', _('Commands')),
470 ('extensions', _('Extensions')),
470 ('extensions', _('Extensions')),
471 ('extensioncommands', _('Extension Commands'))):
471 ('extensioncommands', _('Extension Commands'))):
472 if matches[t]:
472 if matches[t]:
473 rst.append('%s:\n\n' % title)
473 rst.append('%s:\n\n' % title)
474 rst.extend(minirst.maketable(sorted(matches[t]), 1))
474 rst.extend(minirst.maketable(sorted(matches[t]), 1))
475 rst.append('\n')
475 rst.append('\n')
476 if not rst:
476 if not rst:
477 msg = _('no matches')
477 msg = _('no matches')
478 hint = _('try "hg help" for a list of topics')
478 hint = _('try "hg help" for a list of topics')
479 raise util.Abort(msg, hint=hint)
479 raise util.Abort(msg, hint=hint)
480 elif name and name != 'shortlist':
480 elif name and name != 'shortlist':
481 if unknowncmd:
481 if unknowncmd:
482 queries = (helpextcmd,)
482 queries = (helpextcmd,)
483 elif opts.get('extension'):
483 elif opts.get('extension'):
484 queries = (helpext,)
484 queries = (helpext,)
485 elif opts.get('command'):
485 elif opts.get('command'):
486 queries = (helpcmd,)
486 queries = (helpcmd,)
487 else:
487 else:
488 queries = (helptopic, helpcmd, helpext, helpextcmd)
488 queries = (helptopic, helpcmd, helpext, helpextcmd)
489 for f in queries:
489 for f in queries:
490 try:
490 try:
491 rst = f(name)
491 rst = f(name)
492 break
492 break
493 except error.UnknownCommand:
493 except error.UnknownCommand:
494 pass
494 pass
495 else:
495 else:
496 if unknowncmd:
496 if unknowncmd:
497 raise error.UnknownCommand(name)
497 raise error.UnknownCommand(name)
498 else:
498 else:
499 msg = _('no such help topic: %s') % name
499 msg = _('no such help topic: %s') % name
500 hint = _('try "hg help --keyword %s"') % name
500 hint = _('try "hg help --keyword %s"') % name
501 raise util.Abort(msg, hint=hint)
501 raise util.Abort(msg, hint=hint)
502 else:
502 else:
503 # program name
503 # program name
504 if not ui.quiet:
504 if not ui.quiet:
505 rst = [_("Mercurial Distributed SCM\n"), '\n']
505 rst = [_("Mercurial Distributed SCM\n"), '\n']
506 rst.extend(helplist())
506 rst.extend(helplist())
507
507
508 return ''.join(rst)
508 return ''.join(rst)
General Comments 0
You need to be logged in to leave comments. Login now