##// END OF EJS Templates
dispatch: handle empty `testedwith` value in extension...
Pierre-Yves David -
r18224:0f901311 stable
parent child Browse files
Show More
@@ -1,831 +1,831 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', '')
221 report = getattr(mod, 'buglink', _('the extension author.'))
221 report = getattr(mod, 'buglink', _('the extension author.'))
222 if testedwith == 'unknown':
222 if not testedwith.strip():
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, 'unknown', 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 lower = [t for t in tested if t < ct]
228 lower = [t for t in tested if t < ct]
229 nearest = max(lower or tested)
229 nearest = max(lower or tested)
230 if worst[0] is None or nearest < worst[1]:
230 if worst[0] is None or nearest < worst[1]:
231 worst = name, nearest, report
231 worst = name, nearest, report
232 if worst[0] is not None:
232 if worst[0] is not None:
233 name, testedwith, report = worst
233 name, testedwith, report = worst
234 if not isinstance(testedwith, str):
234 if not isinstance(testedwith, str):
235 testedwith = '.'.join([str(c) for c in testedwith])
235 testedwith = '.'.join([str(c) for c in testedwith])
236 warning = (_('** Unknown exception encountered with '
236 warning = (_('** Unknown exception encountered with '
237 'possibly-broken third-party extension %s\n'
237 'possibly-broken third-party extension %s\n'
238 '** which supports versions %s of Mercurial.\n'
238 '** which supports versions %s of Mercurial.\n'
239 '** Please disable %s and try your action again.\n'
239 '** Please disable %s and try your action again.\n'
240 '** If that fixes the bug please report it to %s\n')
240 '** If that fixes the bug please report it to %s\n')
241 % (name, testedwith, name, report))
241 % (name, testedwith, name, report))
242 else:
242 else:
243 warning = (_("** unknown exception encountered, "
243 warning = (_("** unknown exception encountered, "
244 "please report by visiting\n") +
244 "please report by visiting\n") +
245 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
245 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
246 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
246 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
247 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
247 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
248 (_("** Extensions loaded: %s\n") %
248 (_("** Extensions loaded: %s\n") %
249 ", ".join([x[0] for x in extensions.extensions()])))
249 ", ".join([x[0] for x in extensions.extensions()])))
250 ui.warn(warning)
250 ui.warn(warning)
251 raise
251 raise
252
252
253 return -1
253 return -1
254
254
255 def tuplever(v):
255 def tuplever(v):
256 try:
256 try:
257 return tuple([int(i) for i in v.split('.')])
257 return tuple([int(i) for i in v.split('.')])
258 except ValueError:
258 except ValueError:
259 return tuple()
259 return tuple()
260
260
261 def aliasargs(fn, givenargs):
261 def aliasargs(fn, givenargs):
262 args = getattr(fn, 'args', [])
262 args = getattr(fn, 'args', [])
263 if args:
263 if args:
264 cmd = ' '.join(map(util.shellquote, args))
264 cmd = ' '.join(map(util.shellquote, args))
265
265
266 nums = []
266 nums = []
267 def replacer(m):
267 def replacer(m):
268 num = int(m.group(1)) - 1
268 num = int(m.group(1)) - 1
269 nums.append(num)
269 nums.append(num)
270 if num < len(givenargs):
270 if num < len(givenargs):
271 return givenargs[num]
271 return givenargs[num]
272 raise util.Abort(_('too few arguments for command alias'))
272 raise util.Abort(_('too few arguments for command alias'))
273 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
273 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
274 givenargs = [x for i, x in enumerate(givenargs)
274 givenargs = [x for i, x in enumerate(givenargs)
275 if i not in nums]
275 if i not in nums]
276 args = shlex.split(cmd)
276 args = shlex.split(cmd)
277 return args + givenargs
277 return args + givenargs
278
278
279 class cmdalias(object):
279 class cmdalias(object):
280 def __init__(self, name, definition, cmdtable):
280 def __init__(self, name, definition, cmdtable):
281 self.name = self.cmd = name
281 self.name = self.cmd = name
282 self.cmdname = ''
282 self.cmdname = ''
283 self.definition = definition
283 self.definition = definition
284 self.args = []
284 self.args = []
285 self.opts = []
285 self.opts = []
286 self.help = ''
286 self.help = ''
287 self.norepo = True
287 self.norepo = True
288 self.optionalrepo = False
288 self.optionalrepo = False
289 self.badalias = False
289 self.badalias = False
290
290
291 try:
291 try:
292 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
292 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
293 for alias, e in cmdtable.iteritems():
293 for alias, e in cmdtable.iteritems():
294 if e is entry:
294 if e is entry:
295 self.cmd = alias
295 self.cmd = alias
296 break
296 break
297 self.shadows = True
297 self.shadows = True
298 except error.UnknownCommand:
298 except error.UnknownCommand:
299 self.shadows = False
299 self.shadows = False
300
300
301 if not self.definition:
301 if not self.definition:
302 def fn(ui, *args):
302 def fn(ui, *args):
303 ui.warn(_("no definition for alias '%s'\n") % self.name)
303 ui.warn(_("no definition for alias '%s'\n") % self.name)
304 return 1
304 return 1
305 self.fn = fn
305 self.fn = fn
306 self.badalias = True
306 self.badalias = True
307 return
307 return
308
308
309 if self.definition.startswith('!'):
309 if self.definition.startswith('!'):
310 self.shell = True
310 self.shell = True
311 def fn(ui, *args):
311 def fn(ui, *args):
312 env = {'HG_ARGS': ' '.join((self.name,) + args)}
312 env = {'HG_ARGS': ' '.join((self.name,) + args)}
313 def _checkvar(m):
313 def _checkvar(m):
314 if m.groups()[0] == '$':
314 if m.groups()[0] == '$':
315 return m.group()
315 return m.group()
316 elif int(m.groups()[0]) <= len(args):
316 elif int(m.groups()[0]) <= len(args):
317 return m.group()
317 return m.group()
318 else:
318 else:
319 ui.debug("No argument found for substitution "
319 ui.debug("No argument found for substitution "
320 "of %i variable in alias '%s' definition."
320 "of %i variable in alias '%s' definition."
321 % (int(m.groups()[0]), self.name))
321 % (int(m.groups()[0]), self.name))
322 return ''
322 return ''
323 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
323 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
324 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))
325 replace['0'] = self.name
325 replace['0'] = self.name
326 replace['@'] = ' '.join(args)
326 replace['@'] = ' '.join(args)
327 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
327 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
328 return util.system(cmd, environ=env, out=ui.fout)
328 return util.system(cmd, environ=env, out=ui.fout)
329 self.fn = fn
329 self.fn = fn
330 return
330 return
331
331
332 args = shlex.split(self.definition)
332 args = shlex.split(self.definition)
333 self.cmdname = cmd = args.pop(0)
333 self.cmdname = cmd = args.pop(0)
334 args = map(util.expandpath, args)
334 args = map(util.expandpath, args)
335
335
336 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
336 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
337 if _earlygetopt([invalidarg], args):
337 if _earlygetopt([invalidarg], args):
338 def fn(ui, *args):
338 def fn(ui, *args):
339 ui.warn(_("error in definition for alias '%s': %s may only "
339 ui.warn(_("error in definition for alias '%s': %s may only "
340 "be given on the command line\n")
340 "be given on the command line\n")
341 % (self.name, invalidarg))
341 % (self.name, invalidarg))
342 return 1
342 return 1
343
343
344 self.fn = fn
344 self.fn = fn
345 self.badalias = True
345 self.badalias = True
346 return
346 return
347
347
348 try:
348 try:
349 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
349 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
350 if len(tableentry) > 2:
350 if len(tableentry) > 2:
351 self.fn, self.opts, self.help = tableentry
351 self.fn, self.opts, self.help = tableentry
352 else:
352 else:
353 self.fn, self.opts = tableentry
353 self.fn, self.opts = tableentry
354
354
355 self.args = aliasargs(self.fn, args)
355 self.args = aliasargs(self.fn, args)
356 if cmd not in commands.norepo.split(' '):
356 if cmd not in commands.norepo.split(' '):
357 self.norepo = False
357 self.norepo = False
358 if cmd in commands.optionalrepo.split(' '):
358 if cmd in commands.optionalrepo.split(' '):
359 self.optionalrepo = True
359 self.optionalrepo = True
360 if self.help.startswith("hg " + cmd):
360 if self.help.startswith("hg " + cmd):
361 # 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
362 self.help = self.help[4 + len(cmd):]
362 self.help = self.help[4 + len(cmd):]
363 self.__doc__ = self.fn.__doc__
363 self.__doc__ = self.fn.__doc__
364
364
365 except error.UnknownCommand:
365 except error.UnknownCommand:
366 def fn(ui, *args):
366 def fn(ui, *args):
367 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
367 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
368 % (self.name, cmd))
368 % (self.name, cmd))
369 try:
369 try:
370 # check if the command is in a disabled extension
370 # check if the command is in a disabled extension
371 commands.help_(ui, cmd, unknowncmd=True)
371 commands.help_(ui, cmd, unknowncmd=True)
372 except error.UnknownCommand:
372 except error.UnknownCommand:
373 pass
373 pass
374 return 1
374 return 1
375 self.fn = fn
375 self.fn = fn
376 self.badalias = True
376 self.badalias = True
377 except error.AmbiguousCommand:
377 except error.AmbiguousCommand:
378 def fn(ui, *args):
378 def fn(ui, *args):
379 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
379 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
380 % (self.name, cmd))
380 % (self.name, cmd))
381 return 1
381 return 1
382 self.fn = fn
382 self.fn = fn
383 self.badalias = True
383 self.badalias = True
384
384
385 def __call__(self, ui, *args, **opts):
385 def __call__(self, ui, *args, **opts):
386 if self.shadows:
386 if self.shadows:
387 ui.debug("alias '%s' shadows command '%s'\n" %
387 ui.debug("alias '%s' shadows command '%s'\n" %
388 (self.name, self.cmdname))
388 (self.name, self.cmdname))
389
389
390 if util.safehasattr(self, 'shell'):
390 if util.safehasattr(self, 'shell'):
391 return self.fn(ui, *args, **opts)
391 return self.fn(ui, *args, **opts)
392 else:
392 else:
393 try:
393 try:
394 util.checksignature(self.fn)(ui, *args, **opts)
394 util.checksignature(self.fn)(ui, *args, **opts)
395 except error.SignatureError:
395 except error.SignatureError:
396 args = ' '.join([self.cmdname] + self.args)
396 args = ' '.join([self.cmdname] + self.args)
397 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
397 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
398 raise
398 raise
399
399
400 def addaliases(ui, cmdtable):
400 def addaliases(ui, cmdtable):
401 # aliases are processed after extensions have been loaded, so they
401 # aliases are processed after extensions have been loaded, so they
402 # may use extension commands. Aliases can also use other alias definitions,
402 # may use extension commands. Aliases can also use other alias definitions,
403 # 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.
404 for alias, definition in ui.configitems('alias'):
404 for alias, definition in ui.configitems('alias'):
405 aliasdef = cmdalias(alias, definition, cmdtable)
405 aliasdef = cmdalias(alias, definition, cmdtable)
406
406
407 try:
407 try:
408 olddef = cmdtable[aliasdef.cmd][0]
408 olddef = cmdtable[aliasdef.cmd][0]
409 if olddef.definition == aliasdef.definition:
409 if olddef.definition == aliasdef.definition:
410 continue
410 continue
411 except (KeyError, AttributeError):
411 except (KeyError, AttributeError):
412 # definition might not exist or it might not be a cmdalias
412 # definition might not exist or it might not be a cmdalias
413 pass
413 pass
414
414
415 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
415 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
416 if aliasdef.norepo:
416 if aliasdef.norepo:
417 commands.norepo += ' %s' % alias
417 commands.norepo += ' %s' % alias
418 if aliasdef.optionalrepo:
418 if aliasdef.optionalrepo:
419 commands.optionalrepo += ' %s' % alias
419 commands.optionalrepo += ' %s' % alias
420
420
421 def _parse(ui, args):
421 def _parse(ui, args):
422 options = {}
422 options = {}
423 cmdoptions = {}
423 cmdoptions = {}
424
424
425 try:
425 try:
426 args = fancyopts.fancyopts(args, commands.globalopts, options)
426 args = fancyopts.fancyopts(args, commands.globalopts, options)
427 except fancyopts.getopt.GetoptError, inst:
427 except fancyopts.getopt.GetoptError, inst:
428 raise error.CommandError(None, inst)
428 raise error.CommandError(None, inst)
429
429
430 if args:
430 if args:
431 cmd, args = args[0], args[1:]
431 cmd, args = args[0], args[1:]
432 aliases, entry = cmdutil.findcmd(cmd, commands.table,
432 aliases, entry = cmdutil.findcmd(cmd, commands.table,
433 ui.configbool("ui", "strict"))
433 ui.configbool("ui", "strict"))
434 cmd = aliases[0]
434 cmd = aliases[0]
435 args = aliasargs(entry[0], args)
435 args = aliasargs(entry[0], args)
436 defaults = ui.config("defaults", cmd)
436 defaults = ui.config("defaults", cmd)
437 if defaults:
437 if defaults:
438 args = map(util.expandpath, shlex.split(defaults)) + args
438 args = map(util.expandpath, shlex.split(defaults)) + args
439 c = list(entry[1])
439 c = list(entry[1])
440 else:
440 else:
441 cmd = None
441 cmd = None
442 c = []
442 c = []
443
443
444 # combine global options into local
444 # combine global options into local
445 for o in commands.globalopts:
445 for o in commands.globalopts:
446 c.append((o[0], o[1], options[o[1]], o[3]))
446 c.append((o[0], o[1], options[o[1]], o[3]))
447
447
448 try:
448 try:
449 args = fancyopts.fancyopts(args, c, cmdoptions, True)
449 args = fancyopts.fancyopts(args, c, cmdoptions, True)
450 except fancyopts.getopt.GetoptError, inst:
450 except fancyopts.getopt.GetoptError, inst:
451 raise error.CommandError(cmd, inst)
451 raise error.CommandError(cmd, inst)
452
452
453 # separate global options back out
453 # separate global options back out
454 for o in commands.globalopts:
454 for o in commands.globalopts:
455 n = o[1]
455 n = o[1]
456 options[n] = cmdoptions[n]
456 options[n] = cmdoptions[n]
457 del cmdoptions[n]
457 del cmdoptions[n]
458
458
459 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
459 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
460
460
461 def _parseconfig(ui, config):
461 def _parseconfig(ui, config):
462 """parse the --config options from the command line"""
462 """parse the --config options from the command line"""
463 configs = []
463 configs = []
464
464
465 for cfg in config:
465 for cfg in config:
466 try:
466 try:
467 name, value = cfg.split('=', 1)
467 name, value = cfg.split('=', 1)
468 section, name = name.split('.', 1)
468 section, name = name.split('.', 1)
469 if not section or not name:
469 if not section or not name:
470 raise IndexError
470 raise IndexError
471 ui.setconfig(section, name, value)
471 ui.setconfig(section, name, value)
472 configs.append((section, name, value))
472 configs.append((section, name, value))
473 except (IndexError, ValueError):
473 except (IndexError, ValueError):
474 raise util.Abort(_('malformed --config option: %r '
474 raise util.Abort(_('malformed --config option: %r '
475 '(use --config section.name=value)') % cfg)
475 '(use --config section.name=value)') % cfg)
476
476
477 return configs
477 return configs
478
478
479 def _earlygetopt(aliases, args):
479 def _earlygetopt(aliases, args):
480 """Return list of values for an option (or aliases).
480 """Return list of values for an option (or aliases).
481
481
482 The values are listed in the order they appear in args.
482 The values are listed in the order they appear in args.
483 The options and values are removed from args.
483 The options and values are removed from args.
484 """
484 """
485 try:
485 try:
486 argcount = args.index("--")
486 argcount = args.index("--")
487 except ValueError:
487 except ValueError:
488 argcount = len(args)
488 argcount = len(args)
489 shortopts = [opt for opt in aliases if len(opt) == 2]
489 shortopts = [opt for opt in aliases if len(opt) == 2]
490 values = []
490 values = []
491 pos = 0
491 pos = 0
492 while pos < argcount:
492 while pos < argcount:
493 if args[pos] in aliases:
493 if args[pos] in aliases:
494 if pos + 1 >= argcount:
494 if pos + 1 >= argcount:
495 # 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
496 break
496 break
497 del args[pos]
497 del args[pos]
498 values.append(args.pop(pos))
498 values.append(args.pop(pos))
499 argcount -= 2
499 argcount -= 2
500 elif args[pos][:2] in shortopts:
500 elif args[pos][:2] in shortopts:
501 # 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
502 values.append(args.pop(pos)[2:])
502 values.append(args.pop(pos)[2:])
503 argcount -= 1
503 argcount -= 1
504 else:
504 else:
505 pos += 1
505 pos += 1
506 return values
506 return values
507
507
508 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
508 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
509 # run pre-hook, and abort if it fails
509 # run pre-hook, and abort if it fails
510 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),
511 pats=cmdpats, opts=cmdoptions)
511 pats=cmdpats, opts=cmdoptions)
512 if ret:
512 if ret:
513 return ret
513 return ret
514 ret = _runcommand(ui, options, cmd, d)
514 ret = _runcommand(ui, options, cmd, d)
515 # run post-hook, passing command result
515 # run post-hook, passing command result
516 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
516 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
517 result=ret, pats=cmdpats, opts=cmdoptions)
517 result=ret, pats=cmdpats, opts=cmdoptions)
518 return ret
518 return ret
519
519
520 def _getlocal(ui, rpath):
520 def _getlocal(ui, rpath):
521 """Return (path, local ui object) for the given target path.
521 """Return (path, local ui object) for the given target path.
522
522
523 Takes paths in [cwd]/.hg/hgrc into account."
523 Takes paths in [cwd]/.hg/hgrc into account."
524 """
524 """
525 try:
525 try:
526 wd = os.getcwd()
526 wd = os.getcwd()
527 except OSError, e:
527 except OSError, e:
528 raise util.Abort(_("error getting current working directory: %s") %
528 raise util.Abort(_("error getting current working directory: %s") %
529 e.strerror)
529 e.strerror)
530 path = cmdutil.findrepo(wd) or ""
530 path = cmdutil.findrepo(wd) or ""
531 if not path:
531 if not path:
532 lui = ui
532 lui = ui
533 else:
533 else:
534 lui = ui.copy()
534 lui = ui.copy()
535 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
535 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
536
536
537 if rpath and rpath[-1]:
537 if rpath and rpath[-1]:
538 path = lui.expandpath(rpath[-1])
538 path = lui.expandpath(rpath[-1])
539 lui = ui.copy()
539 lui = ui.copy()
540 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
540 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
541
541
542 return path, lui
542 return path, lui
543
543
544 def _checkshellalias(lui, ui, args):
544 def _checkshellalias(lui, ui, args):
545 options = {}
545 options = {}
546
546
547 try:
547 try:
548 args = fancyopts.fancyopts(args, commands.globalopts, options)
548 args = fancyopts.fancyopts(args, commands.globalopts, options)
549 except fancyopts.getopt.GetoptError:
549 except fancyopts.getopt.GetoptError:
550 return
550 return
551
551
552 if not args:
552 if not args:
553 return
553 return
554
554
555 norepo = commands.norepo
555 norepo = commands.norepo
556 optionalrepo = commands.optionalrepo
556 optionalrepo = commands.optionalrepo
557 def restorecommands():
557 def restorecommands():
558 commands.norepo = norepo
558 commands.norepo = norepo
559 commands.optionalrepo = optionalrepo
559 commands.optionalrepo = optionalrepo
560
560
561 cmdtable = commands.table.copy()
561 cmdtable = commands.table.copy()
562 addaliases(lui, cmdtable)
562 addaliases(lui, cmdtable)
563
563
564 cmd = args[0]
564 cmd = args[0]
565 try:
565 try:
566 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
566 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
567 lui.configbool("ui", "strict"))
567 lui.configbool("ui", "strict"))
568 except (error.AmbiguousCommand, error.UnknownCommand):
568 except (error.AmbiguousCommand, error.UnknownCommand):
569 restorecommands()
569 restorecommands()
570 return
570 return
571
571
572 cmd = aliases[0]
572 cmd = aliases[0]
573 fn = entry[0]
573 fn = entry[0]
574
574
575 if cmd and util.safehasattr(fn, 'shell'):
575 if cmd and util.safehasattr(fn, 'shell'):
576 d = lambda: fn(ui, *args[1:])
576 d = lambda: fn(ui, *args[1:])
577 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
577 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
578 [], {})
578 [], {})
579
579
580 restorecommands()
580 restorecommands()
581
581
582 _loaded = set()
582 _loaded = set()
583 def _dispatch(req):
583 def _dispatch(req):
584 args = req.args
584 args = req.args
585 ui = req.ui
585 ui = req.ui
586
586
587 # read --config before doing anything else
587 # read --config before doing anything else
588 # (e.g. to change trust settings for reading .hg/hgrc)
588 # (e.g. to change trust settings for reading .hg/hgrc)
589 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
589 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
590
590
591 # check for cwd
591 # check for cwd
592 cwd = _earlygetopt(['--cwd'], args)
592 cwd = _earlygetopt(['--cwd'], args)
593 if cwd:
593 if cwd:
594 os.chdir(cwd[-1])
594 os.chdir(cwd[-1])
595
595
596 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
596 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
597 path, lui = _getlocal(ui, rpath)
597 path, lui = _getlocal(ui, rpath)
598
598
599 # Now that we're operating in the right directory/repository with
599 # Now that we're operating in the right directory/repository with
600 # the right config settings, check for shell aliases
600 # the right config settings, check for shell aliases
601 shellaliasfn = _checkshellalias(lui, ui, args)
601 shellaliasfn = _checkshellalias(lui, ui, args)
602 if shellaliasfn:
602 if shellaliasfn:
603 return shellaliasfn()
603 return shellaliasfn()
604
604
605 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
605 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
606 # reposetup. Programs like TortoiseHg will call _dispatch several
606 # reposetup. Programs like TortoiseHg will call _dispatch several
607 # times so we keep track of configured extensions in _loaded.
607 # times so we keep track of configured extensions in _loaded.
608 extensions.loadall(lui)
608 extensions.loadall(lui)
609 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]
610 # Propagate any changes to lui.__class__ by extensions
610 # Propagate any changes to lui.__class__ by extensions
611 ui.__class__ = lui.__class__
611 ui.__class__ = lui.__class__
612
612
613 # (uisetup and extsetup are handled in extensions.loadall)
613 # (uisetup and extsetup are handled in extensions.loadall)
614
614
615 for name, module in exts:
615 for name, module in exts:
616 cmdtable = getattr(module, 'cmdtable', {})
616 cmdtable = getattr(module, 'cmdtable', {})
617 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
617 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
618 if overrides:
618 if overrides:
619 ui.warn(_("extension '%s' overrides commands: %s\n")
619 ui.warn(_("extension '%s' overrides commands: %s\n")
620 % (name, " ".join(overrides)))
620 % (name, " ".join(overrides)))
621 commands.table.update(cmdtable)
621 commands.table.update(cmdtable)
622 _loaded.add(name)
622 _loaded.add(name)
623
623
624 # (reposetup is handled in hg.repository)
624 # (reposetup is handled in hg.repository)
625
625
626 addaliases(lui, commands.table)
626 addaliases(lui, commands.table)
627
627
628 # check for fallback encoding
628 # check for fallback encoding
629 fallback = lui.config('ui', 'fallbackencoding')
629 fallback = lui.config('ui', 'fallbackencoding')
630 if fallback:
630 if fallback:
631 encoding.fallbackencoding = fallback
631 encoding.fallbackencoding = fallback
632
632
633 fullargs = args
633 fullargs = args
634 cmd, func, args, options, cmdoptions = _parse(lui, args)
634 cmd, func, args, options, cmdoptions = _parse(lui, args)
635
635
636 if options["config"]:
636 if options["config"]:
637 raise util.Abort(_("option --config may not be abbreviated!"))
637 raise util.Abort(_("option --config may not be abbreviated!"))
638 if options["cwd"]:
638 if options["cwd"]:
639 raise util.Abort(_("option --cwd may not be abbreviated!"))
639 raise util.Abort(_("option --cwd may not be abbreviated!"))
640 if options["repository"]:
640 if options["repository"]:
641 raise util.Abort(_(
641 raise util.Abort(_(
642 "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) "
643 "and --repository may only be abbreviated as --repo!"))
643 "and --repository may only be abbreviated as --repo!"))
644
644
645 if options["encoding"]:
645 if options["encoding"]:
646 encoding.encoding = options["encoding"]
646 encoding.encoding = options["encoding"]
647 if options["encodingmode"]:
647 if options["encodingmode"]:
648 encoding.encodingmode = options["encodingmode"]
648 encoding.encodingmode = options["encodingmode"]
649 if options["time"]:
649 if options["time"]:
650 def get_times():
650 def get_times():
651 t = os.times()
651 t = os.times()
652 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()
653 t = (t[0], t[1], t[2], t[3], time.clock())
653 t = (t[0], t[1], t[2], t[3], time.clock())
654 return t
654 return t
655 s = get_times()
655 s = get_times()
656 def print_time():
656 def print_time():
657 t = get_times()
657 t = get_times()
658 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") %
659 (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]))
660 atexit.register(print_time)
660 atexit.register(print_time)
661
661
662 uis = set([ui, lui])
662 uis = set([ui, lui])
663
663
664 if req.repo:
664 if req.repo:
665 uis.add(req.repo.ui)
665 uis.add(req.repo.ui)
666
666
667 # 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
668 for cfg in cfgs:
668 for cfg in cfgs:
669 req.repo.ui.setconfig(*cfg)
669 req.repo.ui.setconfig(*cfg)
670
670
671 if options['verbose'] or options['debug'] or options['quiet']:
671 if options['verbose'] or options['debug'] or options['quiet']:
672 for opt in ('verbose', 'debug', 'quiet'):
672 for opt in ('verbose', 'debug', 'quiet'):
673 val = str(bool(options[opt]))
673 val = str(bool(options[opt]))
674 for ui_ in uis:
674 for ui_ in uis:
675 ui_.setconfig('ui', opt, val)
675 ui_.setconfig('ui', opt, val)
676
676
677 if options['traceback']:
677 if options['traceback']:
678 for ui_ in uis:
678 for ui_ in uis:
679 ui_.setconfig('ui', 'traceback', 'on')
679 ui_.setconfig('ui', 'traceback', 'on')
680
680
681 if options['noninteractive']:
681 if options['noninteractive']:
682 for ui_ in uis:
682 for ui_ in uis:
683 ui_.setconfig('ui', 'interactive', 'off')
683 ui_.setconfig('ui', 'interactive', 'off')
684
684
685 if cmdoptions.get('insecure', False):
685 if cmdoptions.get('insecure', False):
686 for ui_ in uis:
686 for ui_ in uis:
687 ui_.setconfig('web', 'cacerts', '')
687 ui_.setconfig('web', 'cacerts', '')
688
688
689 if options['version']:
689 if options['version']:
690 return commands.version_(ui)
690 return commands.version_(ui)
691 if options['help']:
691 if options['help']:
692 return commands.help_(ui, cmd)
692 return commands.help_(ui, cmd)
693 elif not cmd:
693 elif not cmd:
694 return commands.help_(ui, 'shortlist')
694 return commands.help_(ui, 'shortlist')
695
695
696 repo = None
696 repo = None
697 cmdpats = args[:]
697 cmdpats = args[:]
698 if cmd not in commands.norepo.split():
698 if cmd not in commands.norepo.split():
699 # 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
700 if not rpath and not cwd:
700 if not rpath and not cwd:
701 repo = req.repo
701 repo = req.repo
702
702
703 if repo:
703 if repo:
704 # set the descriptors of the repo ui to those of ui
704 # set the descriptors of the repo ui to those of ui
705 repo.ui.fin = ui.fin
705 repo.ui.fin = ui.fin
706 repo.ui.fout = ui.fout
706 repo.ui.fout = ui.fout
707 repo.ui.ferr = ui.ferr
707 repo.ui.ferr = ui.ferr
708 else:
708 else:
709 try:
709 try:
710 repo = hg.repository(ui, path=path)
710 repo = hg.repository(ui, path=path)
711 if not repo.local():
711 if not repo.local():
712 raise util.Abort(_("repository '%s' is not local") % path)
712 raise util.Abort(_("repository '%s' is not local") % path)
713 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
713 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
714 except error.RequirementError:
714 except error.RequirementError:
715 raise
715 raise
716 except error.RepoError:
716 except error.RepoError:
717 if cmd not in commands.optionalrepo.split():
717 if cmd not in commands.optionalrepo.split():
718 if (cmd in commands.inferrepo.split() and
718 if (cmd in commands.inferrepo.split() and
719 args and not path): # try to infer -R from command args
719 args and not path): # try to infer -R from command args
720 repos = map(cmdutil.findrepo, args)
720 repos = map(cmdutil.findrepo, args)
721 guess = repos[0]
721 guess = repos[0]
722 if guess and repos.count(guess) == len(repos):
722 if guess and repos.count(guess) == len(repos):
723 req.args = ['--repository', guess] + fullargs
723 req.args = ['--repository', guess] + fullargs
724 return _dispatch(req)
724 return _dispatch(req)
725 if not path:
725 if not path:
726 raise error.RepoError(_("no repository found in '%s'"
726 raise error.RepoError(_("no repository found in '%s'"
727 " (.hg not found)")
727 " (.hg not found)")
728 % os.getcwd())
728 % os.getcwd())
729 raise
729 raise
730 if repo:
730 if repo:
731 ui = repo.ui
731 ui = repo.ui
732 args.insert(0, repo)
732 args.insert(0, repo)
733 elif rpath:
733 elif rpath:
734 ui.warn(_("warning: --repository ignored\n"))
734 ui.warn(_("warning: --repository ignored\n"))
735
735
736 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
736 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
737 ui.log("command", msg + "\n")
737 ui.log("command", msg + "\n")
738 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
738 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
739 try:
739 try:
740 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
740 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
741 cmdpats, cmdoptions)
741 cmdpats, cmdoptions)
742 finally:
742 finally:
743 if repo and repo != req.repo:
743 if repo and repo != req.repo:
744 repo.close()
744 repo.close()
745
745
746 def lsprofile(ui, func, fp):
746 def lsprofile(ui, func, fp):
747 format = ui.config('profiling', 'format', default='text')
747 format = ui.config('profiling', 'format', default='text')
748 field = ui.config('profiling', 'sort', default='inlinetime')
748 field = ui.config('profiling', 'sort', default='inlinetime')
749 climit = ui.configint('profiling', 'nested', default=5)
749 climit = ui.configint('profiling', 'nested', default=5)
750
750
751 if format not in ['text', 'kcachegrind']:
751 if format not in ['text', 'kcachegrind']:
752 ui.warn(_("unrecognized profiling format '%s'"
752 ui.warn(_("unrecognized profiling format '%s'"
753 " - Ignored\n") % format)
753 " - Ignored\n") % format)
754 format = 'text'
754 format = 'text'
755
755
756 try:
756 try:
757 from mercurial import lsprof
757 from mercurial import lsprof
758 except ImportError:
758 except ImportError:
759 raise util.Abort(_(
759 raise util.Abort(_(
760 'lsprof not available - install from '
760 'lsprof not available - install from '
761 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
761 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
762 p = lsprof.Profiler()
762 p = lsprof.Profiler()
763 p.enable(subcalls=True)
763 p.enable(subcalls=True)
764 try:
764 try:
765 return func()
765 return func()
766 finally:
766 finally:
767 p.disable()
767 p.disable()
768
768
769 if format == 'kcachegrind':
769 if format == 'kcachegrind':
770 import lsprofcalltree
770 import lsprofcalltree
771 calltree = lsprofcalltree.KCacheGrind(p)
771 calltree = lsprofcalltree.KCacheGrind(p)
772 calltree.output(fp)
772 calltree.output(fp)
773 else:
773 else:
774 # format == 'text'
774 # format == 'text'
775 stats = lsprof.Stats(p.getstats())
775 stats = lsprof.Stats(p.getstats())
776 stats.sort(field)
776 stats.sort(field)
777 stats.pprint(limit=30, file=fp, climit=climit)
777 stats.pprint(limit=30, file=fp, climit=climit)
778
778
779 def statprofile(ui, func, fp):
779 def statprofile(ui, func, fp):
780 try:
780 try:
781 import statprof
781 import statprof
782 except ImportError:
782 except ImportError:
783 raise util.Abort(_(
783 raise util.Abort(_(
784 'statprof not available - install using "easy_install statprof"'))
784 'statprof not available - install using "easy_install statprof"'))
785
785
786 freq = ui.configint('profiling', 'freq', default=1000)
786 freq = ui.configint('profiling', 'freq', default=1000)
787 if freq > 0:
787 if freq > 0:
788 statprof.reset(freq)
788 statprof.reset(freq)
789 else:
789 else:
790 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
790 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
791
791
792 statprof.start()
792 statprof.start()
793 try:
793 try:
794 return func()
794 return func()
795 finally:
795 finally:
796 statprof.stop()
796 statprof.stop()
797 statprof.display(fp)
797 statprof.display(fp)
798
798
799 def _runcommand(ui, options, cmd, cmdfunc):
799 def _runcommand(ui, options, cmd, cmdfunc):
800 def checkargs():
800 def checkargs():
801 try:
801 try:
802 return cmdfunc()
802 return cmdfunc()
803 except error.SignatureError:
803 except error.SignatureError:
804 raise error.CommandError(cmd, _("invalid arguments"))
804 raise error.CommandError(cmd, _("invalid arguments"))
805
805
806 if options['profile']:
806 if options['profile']:
807 profiler = os.getenv('HGPROF')
807 profiler = os.getenv('HGPROF')
808 if profiler is None:
808 if profiler is None:
809 profiler = ui.config('profiling', 'type', default='ls')
809 profiler = ui.config('profiling', 'type', default='ls')
810 if profiler not in ('ls', 'stat'):
810 if profiler not in ('ls', 'stat'):
811 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
811 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
812 profiler = 'ls'
812 profiler = 'ls'
813
813
814 output = ui.config('profiling', 'output')
814 output = ui.config('profiling', 'output')
815
815
816 if output:
816 if output:
817 path = ui.expandpath(output)
817 path = ui.expandpath(output)
818 fp = open(path, 'wb')
818 fp = open(path, 'wb')
819 else:
819 else:
820 fp = sys.stderr
820 fp = sys.stderr
821
821
822 try:
822 try:
823 if profiler == 'ls':
823 if profiler == 'ls':
824 return lsprofile(ui, checkargs, fp)
824 return lsprofile(ui, checkargs, fp)
825 else:
825 else:
826 return statprofile(ui, checkargs, fp)
826 return statprofile(ui, checkargs, fp)
827 finally:
827 finally:
828 if output:
828 if output:
829 fp.close()
829 fp.close()
830 else:
830 else:
831 return checkargs()
831 return checkargs()
@@ -1,561 +1,571 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 the global options
272 use "hg -v help extdiff" to show the global options
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 the global options
375 use "hg -v help multirevs" to show the global options
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 empty declaration of supported version, extension complains:
502 $ echo "testedwith = ''" >> throw.py
503 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
504 ** Unknown exception encountered with possibly-broken third-party extension throw
505 ** which supports versions unknown of Mercurial.
506 ** Please disable throw and try your action again.
507 ** If that fixes the bug please report it to the extension author.
508 ** Python * (glob)
509 ** Mercurial Distributed SCM (*) (glob)
510 ** Extensions loaded: throw
501 If the extension specifies a buglink, show that:
511 If the extension specifies a buglink, show that:
502 $ echo 'buglink = "http://example.com/bts"' >> throw.py
512 $ echo 'buglink = "http://example.com/bts"' >> throw.py
503 $ rm -f throw.pyc throw.pyo
513 $ rm -f throw.pyc throw.pyo
504 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
514 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
505 ** Unknown exception encountered with possibly-broken third-party extension throw
515 ** Unknown exception encountered with possibly-broken third-party extension throw
506 ** which supports versions unknown of Mercurial.
516 ** which supports versions unknown of Mercurial.
507 ** Please disable throw and try your action again.
517 ** Please disable throw and try your action again.
508 ** If that fixes the bug please report it to http://example.com/bts
518 ** If that fixes the bug please report it to http://example.com/bts
509 ** Python * (glob)
519 ** Python * (glob)
510 ** Mercurial Distributed SCM (*) (glob)
520 ** Mercurial Distributed SCM (*) (glob)
511 ** Extensions loaded: throw
521 ** Extensions loaded: throw
512 If the extensions declare outdated versions, accuse the older extension first:
522 If the extensions declare outdated versions, accuse the older extension first:
513 $ echo "from mercurial import util" >> older.py
523 $ echo "from mercurial import util" >> older.py
514 $ echo "util.version = lambda:'2.2'" >> older.py
524 $ echo "util.version = lambda:'2.2'" >> older.py
515 $ echo "testedwith = '1.9.3'" >> older.py
525 $ echo "testedwith = '1.9.3'" >> older.py
516 $ echo "testedwith = '2.1.1'" >> throw.py
526 $ echo "testedwith = '2.1.1'" >> throw.py
517 $ rm -f throw.pyc throw.pyo
527 $ rm -f throw.pyc throw.pyo
518 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
528 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
519 > throw 2>&1 | egrep '^\*\*'
529 > throw 2>&1 | egrep '^\*\*'
520 ** Unknown exception encountered with possibly-broken third-party extension older
530 ** Unknown exception encountered with possibly-broken third-party extension older
521 ** which supports versions 1.9.3 of Mercurial.
531 ** which supports versions 1.9.3 of Mercurial.
522 ** Please disable older and try your action again.
532 ** Please disable older and try your action again.
523 ** If that fixes the bug please report it to the extension author.
533 ** If that fixes the bug please report it to the extension author.
524 ** Python * (glob)
534 ** Python * (glob)
525 ** Mercurial Distributed SCM (version 2.2)
535 ** Mercurial Distributed SCM (version 2.2)
526 ** Extensions loaded: throw, older
536 ** Extensions loaded: throw, older
527 One extension only tested with older, one only with newer versions:
537 One extension only tested with older, one only with newer versions:
528 $ echo "util.version = lambda:'2.1.0'" >> older.py
538 $ echo "util.version = lambda:'2.1.0'" >> older.py
529 $ rm -f older.pyc older.pyo
539 $ rm -f older.pyc older.pyo
530 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
540 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
531 > throw 2>&1 | egrep '^\*\*'
541 > throw 2>&1 | egrep '^\*\*'
532 ** Unknown exception encountered with possibly-broken third-party extension older
542 ** Unknown exception encountered with possibly-broken third-party extension older
533 ** which supports versions 1.9.3 of Mercurial.
543 ** which supports versions 1.9.3 of Mercurial.
534 ** Please disable older and try your action again.
544 ** Please disable older and try your action again.
535 ** If that fixes the bug please report it to the extension author.
545 ** If that fixes the bug please report it to the extension author.
536 ** Python * (glob)
546 ** Python * (glob)
537 ** Mercurial Distributed SCM (version 2.1.0)
547 ** Mercurial Distributed SCM (version 2.1.0)
538 ** Extensions loaded: throw, older
548 ** Extensions loaded: throw, older
539 Older extension is tested with current version, the other only with newer:
549 Older extension is tested with current version, the other only with newer:
540 $ echo "util.version = lambda:'1.9.3'" >> older.py
550 $ echo "util.version = lambda:'1.9.3'" >> older.py
541 $ rm -f older.pyc older.pyo
551 $ rm -f older.pyc older.pyo
542 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
552 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
543 > throw 2>&1 | egrep '^\*\*'
553 > throw 2>&1 | egrep '^\*\*'
544 ** Unknown exception encountered with possibly-broken third-party extension throw
554 ** Unknown exception encountered with possibly-broken third-party extension throw
545 ** which supports versions 2.1.1 of Mercurial.
555 ** which supports versions 2.1.1 of Mercurial.
546 ** Please disable throw and try your action again.
556 ** Please disable throw and try your action again.
547 ** If that fixes the bug please report it to http://example.com/bts
557 ** If that fixes the bug please report it to http://example.com/bts
548 ** Python * (glob)
558 ** Python * (glob)
549 ** Mercurial Distributed SCM (version 1.9.3)
559 ** Mercurial Distributed SCM (version 1.9.3)
550 ** Extensions loaded: throw, older
560 ** Extensions loaded: throw, older
551
561
552 Declare the version as supporting this hg version, show regular bts link:
562 Declare the version as supporting this hg version, show regular bts link:
553 $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'`
563 $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'`
554 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
564 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
555 $ rm -f throw.pyc throw.pyo
565 $ rm -f throw.pyc throw.pyo
556 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
566 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
557 ** unknown exception encountered, please report by visiting
567 ** unknown exception encountered, please report by visiting
558 ** http://mercurial.selenic.com/wiki/BugTracker
568 ** http://mercurial.selenic.com/wiki/BugTracker
559 ** Python * (glob)
569 ** Python * (glob)
560 ** Mercurial Distributed SCM (*) (glob)
570 ** Mercurial Distributed SCM (*) (glob)
561 ** Extensions loaded: throw
571 ** Extensions loaded: throw
General Comments 0
You need to be logged in to leave comments. Login now