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