##// END OF EJS Templates
blackbox: fix recording exit codes (issue3938)...
Durham Goode -
r19229:41e39a02 stable
parent child Browse files
Show More
@@ -1,866 +1,869 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 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
66 starttime = time.time()
67 ret = None
68 try:
69 ret = _runcatch(req)
70 return ret
71 finally:
72 duration = time.time() - starttime
73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
74 msg, ret or 0, duration)
66
75
67 def _runcatch(req):
76 def _runcatch(req):
68 def catchterm(*args):
77 def catchterm(*args):
69 raise error.SignalInterrupt
78 raise error.SignalInterrupt
70
79
71 ui = req.ui
80 ui = req.ui
72 try:
81 try:
73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
74 num = getattr(signal, name, None)
83 num = getattr(signal, name, None)
75 if num:
84 if num:
76 signal.signal(num, catchterm)
85 signal.signal(num, catchterm)
77 except ValueError:
86 except ValueError:
78 pass # happens if called in a thread
87 pass # happens if called in a thread
79
88
80 try:
89 try:
81 try:
90 try:
82 # enter the debugger before command execution
91 # enter the debugger before command execution
83 if '--debugger' in req.args:
92 if '--debugger' in req.args:
84 ui.warn(_("entering debugger - "
93 ui.warn(_("entering debugger - "
85 "type c to continue starting hg or h for help\n"))
94 "type c to continue starting hg or h for help\n"))
86 pdb.set_trace()
95 pdb.set_trace()
87 try:
96 try:
88 return _dispatch(req)
97 return _dispatch(req)
89 finally:
98 finally:
90 ui.flush()
99 ui.flush()
91 except: # re-raises
100 except: # re-raises
92 # enter the debugger when we hit an exception
101 # enter the debugger when we hit an exception
93 if '--debugger' in req.args:
102 if '--debugger' in req.args:
94 traceback.print_exc()
103 traceback.print_exc()
95 pdb.post_mortem(sys.exc_info()[2])
104 pdb.post_mortem(sys.exc_info()[2])
96 ui.traceback()
105 ui.traceback()
97 raise
106 raise
98
107
99 # Global exception handling, alphabetically
108 # Global exception handling, alphabetically
100 # Mercurial-specific first, followed by built-in and library exceptions
109 # Mercurial-specific first, followed by built-in and library exceptions
101 except error.AmbiguousCommand, inst:
110 except error.AmbiguousCommand, inst:
102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
111 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
103 (inst.args[0], " ".join(inst.args[1])))
112 (inst.args[0], " ".join(inst.args[1])))
104 except error.ParseError, inst:
113 except error.ParseError, inst:
105 if len(inst.args) > 1:
114 if len(inst.args) > 1:
106 ui.warn(_("hg: parse error at %s: %s\n") %
115 ui.warn(_("hg: parse error at %s: %s\n") %
107 (inst.args[1], inst.args[0]))
116 (inst.args[1], inst.args[0]))
108 else:
117 else:
109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
118 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
110 return -1
119 return -1
111 except error.LockHeld, inst:
120 except error.LockHeld, inst:
112 if inst.errno == errno.ETIMEDOUT:
121 if inst.errno == errno.ETIMEDOUT:
113 reason = _('timed out waiting for lock held by %s') % inst.locker
122 reason = _('timed out waiting for lock held by %s') % inst.locker
114 else:
123 else:
115 reason = _('lock held by %s') % inst.locker
124 reason = _('lock held by %s') % inst.locker
116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
125 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
117 except error.LockUnavailable, inst:
126 except error.LockUnavailable, inst:
118 ui.warn(_("abort: could not lock %s: %s\n") %
127 ui.warn(_("abort: could not lock %s: %s\n") %
119 (inst.desc or inst.filename, inst.strerror))
128 (inst.desc or inst.filename, inst.strerror))
120 except error.CommandError, inst:
129 except error.CommandError, inst:
121 if inst.args[0]:
130 if inst.args[0]:
122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
131 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
123 commands.help_(ui, inst.args[0], full=False, command=True)
132 commands.help_(ui, inst.args[0], full=False, command=True)
124 else:
133 else:
125 ui.warn(_("hg: %s\n") % inst.args[1])
134 ui.warn(_("hg: %s\n") % inst.args[1])
126 commands.help_(ui, 'shortlist')
135 commands.help_(ui, 'shortlist')
127 except error.OutOfBandError, inst:
136 except error.OutOfBandError, inst:
128 ui.warn(_("abort: remote error:\n"))
137 ui.warn(_("abort: remote error:\n"))
129 ui.warn(''.join(inst.args))
138 ui.warn(''.join(inst.args))
130 except error.RepoError, inst:
139 except error.RepoError, inst:
131 ui.warn(_("abort: %s!\n") % inst)
140 ui.warn(_("abort: %s!\n") % inst)
132 if inst.hint:
141 if inst.hint:
133 ui.warn(_("(%s)\n") % inst.hint)
142 ui.warn(_("(%s)\n") % inst.hint)
134 except error.ResponseError, inst:
143 except error.ResponseError, inst:
135 ui.warn(_("abort: %s") % inst.args[0])
144 ui.warn(_("abort: %s") % inst.args[0])
136 if not isinstance(inst.args[1], basestring):
145 if not isinstance(inst.args[1], basestring):
137 ui.warn(" %r\n" % (inst.args[1],))
146 ui.warn(" %r\n" % (inst.args[1],))
138 elif not inst.args[1]:
147 elif not inst.args[1]:
139 ui.warn(_(" empty string\n"))
148 ui.warn(_(" empty string\n"))
140 else:
149 else:
141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
150 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
142 except error.RevlogError, inst:
151 except error.RevlogError, inst:
143 ui.warn(_("abort: %s!\n") % inst)
152 ui.warn(_("abort: %s!\n") % inst)
144 except error.SignalInterrupt:
153 except error.SignalInterrupt:
145 ui.warn(_("killed!\n"))
154 ui.warn(_("killed!\n"))
146 except error.UnknownCommand, inst:
155 except error.UnknownCommand, inst:
147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
156 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
148 try:
157 try:
149 # check if the command is in a disabled extension
158 # check if the command is in a disabled extension
150 # (but don't check for extensions themselves)
159 # (but don't check for extensions themselves)
151 commands.help_(ui, inst.args[0], unknowncmd=True)
160 commands.help_(ui, inst.args[0], unknowncmd=True)
152 except error.UnknownCommand:
161 except error.UnknownCommand:
153 commands.help_(ui, 'shortlist')
162 commands.help_(ui, 'shortlist')
154 except error.InterventionRequired, inst:
163 except error.InterventionRequired, inst:
155 ui.warn("%s\n" % inst)
164 ui.warn("%s\n" % inst)
156 return 1
165 return 1
157 except util.Abort, inst:
166 except util.Abort, inst:
158 ui.warn(_("abort: %s\n") % inst)
167 ui.warn(_("abort: %s\n") % inst)
159 if inst.hint:
168 if inst.hint:
160 ui.warn(_("(%s)\n") % inst.hint)
169 ui.warn(_("(%s)\n") % inst.hint)
161 except ImportError, inst:
170 except ImportError, inst:
162 ui.warn(_("abort: %s!\n") % inst)
171 ui.warn(_("abort: %s!\n") % inst)
163 m = str(inst).split()[-1]
172 m = str(inst).split()[-1]
164 if m in "mpatch bdiff".split():
173 if m in "mpatch bdiff".split():
165 ui.warn(_("(did you forget to compile extensions?)\n"))
174 ui.warn(_("(did you forget to compile extensions?)\n"))
166 elif m in "zlib".split():
175 elif m in "zlib".split():
167 ui.warn(_("(is your Python install correct?)\n"))
176 ui.warn(_("(is your Python install correct?)\n"))
168 except IOError, inst:
177 except IOError, inst:
169 if util.safehasattr(inst, "code"):
178 if util.safehasattr(inst, "code"):
170 ui.warn(_("abort: %s\n") % inst)
179 ui.warn(_("abort: %s\n") % inst)
171 elif util.safehasattr(inst, "reason"):
180 elif util.safehasattr(inst, "reason"):
172 try: # usually it is in the form (errno, strerror)
181 try: # usually it is in the form (errno, strerror)
173 reason = inst.reason.args[1]
182 reason = inst.reason.args[1]
174 except (AttributeError, IndexError):
183 except (AttributeError, IndexError):
175 # it might be anything, for example a string
184 # it might be anything, for example a string
176 reason = inst.reason
185 reason = inst.reason
177 ui.warn(_("abort: error: %s\n") % reason)
186 ui.warn(_("abort: error: %s\n") % reason)
178 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
187 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
179 if ui.debugflag:
188 if ui.debugflag:
180 ui.warn(_("broken pipe\n"))
189 ui.warn(_("broken pipe\n"))
181 elif getattr(inst, "strerror", None):
190 elif getattr(inst, "strerror", None):
182 if getattr(inst, "filename", None):
191 if getattr(inst, "filename", None):
183 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
192 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
184 else:
193 else:
185 ui.warn(_("abort: %s\n") % inst.strerror)
194 ui.warn(_("abort: %s\n") % inst.strerror)
186 else:
195 else:
187 raise
196 raise
188 except OSError, inst:
197 except OSError, inst:
189 if getattr(inst, "filename", None) is not None:
198 if getattr(inst, "filename", None) is not None:
190 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
199 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
191 else:
200 else:
192 ui.warn(_("abort: %s\n") % inst.strerror)
201 ui.warn(_("abort: %s\n") % inst.strerror)
193 except KeyboardInterrupt:
202 except KeyboardInterrupt:
194 try:
203 try:
195 ui.warn(_("interrupted!\n"))
204 ui.warn(_("interrupted!\n"))
196 except IOError, inst:
205 except IOError, inst:
197 if inst.errno == errno.EPIPE:
206 if inst.errno == errno.EPIPE:
198 if ui.debugflag:
207 if ui.debugflag:
199 ui.warn(_("\nbroken pipe\n"))
208 ui.warn(_("\nbroken pipe\n"))
200 else:
209 else:
201 raise
210 raise
202 except MemoryError:
211 except MemoryError:
203 ui.warn(_("abort: out of memory\n"))
212 ui.warn(_("abort: out of memory\n"))
204 except SystemExit, inst:
213 except SystemExit, inst:
205 # Commands shouldn't sys.exit directly, but give a return code.
214 # Commands shouldn't sys.exit directly, but give a return code.
206 # Just in case catch this and and pass exit code to caller.
215 # Just in case catch this and and pass exit code to caller.
207 return inst.code
216 return inst.code
208 except socket.error, inst:
217 except socket.error, inst:
209 ui.warn(_("abort: %s\n") % inst.args[-1])
218 ui.warn(_("abort: %s\n") % inst.args[-1])
210 except: # re-raises
219 except: # re-raises
211 myver = util.version()
220 myver = util.version()
212 # For compatibility checking, we discard the portion of the hg
221 # For compatibility checking, we discard the portion of the hg
213 # version after the + on the assumption that if a "normal
222 # version after the + on the assumption that if a "normal
214 # user" is running a build with a + in it the packager
223 # user" is running a build with a + in it the packager
215 # probably built from fairly close to a tag and anyone with a
224 # probably built from fairly close to a tag and anyone with a
216 # 'make local' copy of hg (where the version number can be out
225 # 'make local' copy of hg (where the version number can be out
217 # of date) will be clueful enough to notice the implausible
226 # of date) will be clueful enough to notice the implausible
218 # version number and try updating.
227 # version number and try updating.
219 compare = myver.split('+')[0]
228 compare = myver.split('+')[0]
220 ct = tuplever(compare)
229 ct = tuplever(compare)
221 worst = None, ct, ''
230 worst = None, ct, ''
222 for name, mod in extensions.extensions():
231 for name, mod in extensions.extensions():
223 testedwith = getattr(mod, 'testedwith', '')
232 testedwith = getattr(mod, 'testedwith', '')
224 report = getattr(mod, 'buglink', _('the extension author.'))
233 report = getattr(mod, 'buglink', _('the extension author.'))
225 if not testedwith.strip():
234 if not testedwith.strip():
226 # We found an untested extension. It's likely the culprit.
235 # We found an untested extension. It's likely the culprit.
227 worst = name, 'unknown', report
236 worst = name, 'unknown', report
228 break
237 break
229 if compare not in testedwith.split() and testedwith != 'internal':
238 if compare not in testedwith.split() and testedwith != 'internal':
230 tested = [tuplever(v) for v in testedwith.split()]
239 tested = [tuplever(v) for v in testedwith.split()]
231 lower = [t for t in tested if t < ct]
240 lower = [t for t in tested if t < ct]
232 nearest = max(lower or tested)
241 nearest = max(lower or tested)
233 if worst[0] is None or nearest < worst[1]:
242 if worst[0] is None or nearest < worst[1]:
234 worst = name, nearest, report
243 worst = name, nearest, report
235 if worst[0] is not None:
244 if worst[0] is not None:
236 name, testedwith, report = worst
245 name, testedwith, report = worst
237 if not isinstance(testedwith, str):
246 if not isinstance(testedwith, str):
238 testedwith = '.'.join([str(c) for c in testedwith])
247 testedwith = '.'.join([str(c) for c in testedwith])
239 warning = (_('** Unknown exception encountered with '
248 warning = (_('** Unknown exception encountered with '
240 'possibly-broken third-party extension %s\n'
249 'possibly-broken third-party extension %s\n'
241 '** which supports versions %s of Mercurial.\n'
250 '** which supports versions %s of Mercurial.\n'
242 '** Please disable %s and try your action again.\n'
251 '** Please disable %s and try your action again.\n'
243 '** If that fixes the bug please report it to %s\n')
252 '** If that fixes the bug please report it to %s\n')
244 % (name, testedwith, name, report))
253 % (name, testedwith, name, report))
245 else:
254 else:
246 warning = (_("** unknown exception encountered, "
255 warning = (_("** unknown exception encountered, "
247 "please report by visiting\n") +
256 "please report by visiting\n") +
248 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
257 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
249 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
258 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
250 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
259 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
251 (_("** Extensions loaded: %s\n") %
260 (_("** Extensions loaded: %s\n") %
252 ", ".join([x[0] for x in extensions.extensions()])))
261 ", ".join([x[0] for x in extensions.extensions()])))
253 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
262 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
254 ui.warn(warning)
263 ui.warn(warning)
255 raise
264 raise
256
265
257 return -1
266 return -1
258
267
259 def tuplever(v):
268 def tuplever(v):
260 try:
269 try:
261 return tuple([int(i) for i in v.split('.')])
270 return tuple([int(i) for i in v.split('.')])
262 except ValueError:
271 except ValueError:
263 return tuple()
272 return tuple()
264
273
265 def aliasargs(fn, givenargs):
274 def aliasargs(fn, givenargs):
266 args = getattr(fn, 'args', [])
275 args = getattr(fn, 'args', [])
267 if args:
276 if args:
268 cmd = ' '.join(map(util.shellquote, args))
277 cmd = ' '.join(map(util.shellquote, args))
269
278
270 nums = []
279 nums = []
271 def replacer(m):
280 def replacer(m):
272 num = int(m.group(1)) - 1
281 num = int(m.group(1)) - 1
273 nums.append(num)
282 nums.append(num)
274 if num < len(givenargs):
283 if num < len(givenargs):
275 return givenargs[num]
284 return givenargs[num]
276 raise util.Abort(_('too few arguments for command alias'))
285 raise util.Abort(_('too few arguments for command alias'))
277 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
286 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
278 givenargs = [x for i, x in enumerate(givenargs)
287 givenargs = [x for i, x in enumerate(givenargs)
279 if i not in nums]
288 if i not in nums]
280 args = shlex.split(cmd)
289 args = shlex.split(cmd)
281 return args + givenargs
290 return args + givenargs
282
291
283 class cmdalias(object):
292 class cmdalias(object):
284 def __init__(self, name, definition, cmdtable):
293 def __init__(self, name, definition, cmdtable):
285 self.name = self.cmd = name
294 self.name = self.cmd = name
286 self.cmdname = ''
295 self.cmdname = ''
287 self.definition = definition
296 self.definition = definition
288 self.args = []
297 self.args = []
289 self.opts = []
298 self.opts = []
290 self.help = ''
299 self.help = ''
291 self.norepo = True
300 self.norepo = True
292 self.optionalrepo = False
301 self.optionalrepo = False
293 self.badalias = False
302 self.badalias = False
294
303
295 try:
304 try:
296 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
305 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
297 for alias, e in cmdtable.iteritems():
306 for alias, e in cmdtable.iteritems():
298 if e is entry:
307 if e is entry:
299 self.cmd = alias
308 self.cmd = alias
300 break
309 break
301 self.shadows = True
310 self.shadows = True
302 except error.UnknownCommand:
311 except error.UnknownCommand:
303 self.shadows = False
312 self.shadows = False
304
313
305 if not self.definition:
314 if not self.definition:
306 def fn(ui, *args):
315 def fn(ui, *args):
307 ui.warn(_("no definition for alias '%s'\n") % self.name)
316 ui.warn(_("no definition for alias '%s'\n") % self.name)
308 return 1
317 return 1
309 self.fn = fn
318 self.fn = fn
310 self.badalias = True
319 self.badalias = True
311 return
320 return
312
321
313 if self.definition.startswith('!'):
322 if self.definition.startswith('!'):
314 self.shell = True
323 self.shell = True
315 def fn(ui, *args):
324 def fn(ui, *args):
316 env = {'HG_ARGS': ' '.join((self.name,) + args)}
325 env = {'HG_ARGS': ' '.join((self.name,) + args)}
317 def _checkvar(m):
326 def _checkvar(m):
318 if m.groups()[0] == '$':
327 if m.groups()[0] == '$':
319 return m.group()
328 return m.group()
320 elif int(m.groups()[0]) <= len(args):
329 elif int(m.groups()[0]) <= len(args):
321 return m.group()
330 return m.group()
322 else:
331 else:
323 ui.debug("No argument found for substitution "
332 ui.debug("No argument found for substitution "
324 "of %i variable in alias '%s' definition."
333 "of %i variable in alias '%s' definition."
325 % (int(m.groups()[0]), self.name))
334 % (int(m.groups()[0]), self.name))
326 return ''
335 return ''
327 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
336 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
328 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
337 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
329 replace['0'] = self.name
338 replace['0'] = self.name
330 replace['@'] = ' '.join(args)
339 replace['@'] = ' '.join(args)
331 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
340 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
332 return util.system(cmd, environ=env, out=ui.fout)
341 return util.system(cmd, environ=env, out=ui.fout)
333 self.fn = fn
342 self.fn = fn
334 return
343 return
335
344
336 args = shlex.split(self.definition)
345 args = shlex.split(self.definition)
337 self.cmdname = cmd = args.pop(0)
346 self.cmdname = cmd = args.pop(0)
338 args = map(util.expandpath, args)
347 args = map(util.expandpath, args)
339
348
340 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
349 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
341 if _earlygetopt([invalidarg], args):
350 if _earlygetopt([invalidarg], args):
342 def fn(ui, *args):
351 def fn(ui, *args):
343 ui.warn(_("error in definition for alias '%s': %s may only "
352 ui.warn(_("error in definition for alias '%s': %s may only "
344 "be given on the command line\n")
353 "be given on the command line\n")
345 % (self.name, invalidarg))
354 % (self.name, invalidarg))
346 return 1
355 return 1
347
356
348 self.fn = fn
357 self.fn = fn
349 self.badalias = True
358 self.badalias = True
350 return
359 return
351
360
352 try:
361 try:
353 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
362 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
354 if len(tableentry) > 2:
363 if len(tableentry) > 2:
355 self.fn, self.opts, self.help = tableentry
364 self.fn, self.opts, self.help = tableentry
356 else:
365 else:
357 self.fn, self.opts = tableentry
366 self.fn, self.opts = tableentry
358
367
359 self.args = aliasargs(self.fn, args)
368 self.args = aliasargs(self.fn, args)
360 if cmd not in commands.norepo.split(' '):
369 if cmd not in commands.norepo.split(' '):
361 self.norepo = False
370 self.norepo = False
362 if cmd in commands.optionalrepo.split(' '):
371 if cmd in commands.optionalrepo.split(' '):
363 self.optionalrepo = True
372 self.optionalrepo = True
364 if self.help.startswith("hg " + cmd):
373 if self.help.startswith("hg " + cmd):
365 # drop prefix in old-style help lines so hg shows the alias
374 # drop prefix in old-style help lines so hg shows the alias
366 self.help = self.help[4 + len(cmd):]
375 self.help = self.help[4 + len(cmd):]
367 self.__doc__ = self.fn.__doc__
376 self.__doc__ = self.fn.__doc__
368
377
369 except error.UnknownCommand:
378 except error.UnknownCommand:
370 def fn(ui, *args):
379 def fn(ui, *args):
371 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
380 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
372 % (self.name, cmd))
381 % (self.name, cmd))
373 try:
382 try:
374 # check if the command is in a disabled extension
383 # check if the command is in a disabled extension
375 commands.help_(ui, cmd, unknowncmd=True)
384 commands.help_(ui, cmd, unknowncmd=True)
376 except error.UnknownCommand:
385 except error.UnknownCommand:
377 pass
386 pass
378 return 1
387 return 1
379 self.fn = fn
388 self.fn = fn
380 self.badalias = True
389 self.badalias = True
381 except error.AmbiguousCommand:
390 except error.AmbiguousCommand:
382 def fn(ui, *args):
391 def fn(ui, *args):
383 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
392 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
384 % (self.name, cmd))
393 % (self.name, cmd))
385 return 1
394 return 1
386 self.fn = fn
395 self.fn = fn
387 self.badalias = True
396 self.badalias = True
388
397
389 def __call__(self, ui, *args, **opts):
398 def __call__(self, ui, *args, **opts):
390 if self.shadows:
399 if self.shadows:
391 ui.debug("alias '%s' shadows command '%s'\n" %
400 ui.debug("alias '%s' shadows command '%s'\n" %
392 (self.name, self.cmdname))
401 (self.name, self.cmdname))
393
402
394 if util.safehasattr(self, 'shell'):
403 if util.safehasattr(self, 'shell'):
395 return self.fn(ui, *args, **opts)
404 return self.fn(ui, *args, **opts)
396 else:
405 else:
397 try:
406 try:
398 util.checksignature(self.fn)(ui, *args, **opts)
407 util.checksignature(self.fn)(ui, *args, **opts)
399 except error.SignatureError:
408 except error.SignatureError:
400 args = ' '.join([self.cmdname] + self.args)
409 args = ' '.join([self.cmdname] + self.args)
401 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
410 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
402 raise
411 raise
403
412
404 def addaliases(ui, cmdtable):
413 def addaliases(ui, cmdtable):
405 # aliases are processed after extensions have been loaded, so they
414 # aliases are processed after extensions have been loaded, so they
406 # may use extension commands. Aliases can also use other alias definitions,
415 # may use extension commands. Aliases can also use other alias definitions,
407 # but only if they have been defined prior to the current definition.
416 # but only if they have been defined prior to the current definition.
408 for alias, definition in ui.configitems('alias'):
417 for alias, definition in ui.configitems('alias'):
409 aliasdef = cmdalias(alias, definition, cmdtable)
418 aliasdef = cmdalias(alias, definition, cmdtable)
410
419
411 try:
420 try:
412 olddef = cmdtable[aliasdef.cmd][0]
421 olddef = cmdtable[aliasdef.cmd][0]
413 if olddef.definition == aliasdef.definition:
422 if olddef.definition == aliasdef.definition:
414 continue
423 continue
415 except (KeyError, AttributeError):
424 except (KeyError, AttributeError):
416 # definition might not exist or it might not be a cmdalias
425 # definition might not exist or it might not be a cmdalias
417 pass
426 pass
418
427
419 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
428 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
420 if aliasdef.norepo:
429 if aliasdef.norepo:
421 commands.norepo += ' %s' % alias
430 commands.norepo += ' %s' % alias
422 if aliasdef.optionalrepo:
431 if aliasdef.optionalrepo:
423 commands.optionalrepo += ' %s' % alias
432 commands.optionalrepo += ' %s' % alias
424
433
425 def _parse(ui, args):
434 def _parse(ui, args):
426 options = {}
435 options = {}
427 cmdoptions = {}
436 cmdoptions = {}
428
437
429 try:
438 try:
430 args = fancyopts.fancyopts(args, commands.globalopts, options)
439 args = fancyopts.fancyopts(args, commands.globalopts, options)
431 except fancyopts.getopt.GetoptError, inst:
440 except fancyopts.getopt.GetoptError, inst:
432 raise error.CommandError(None, inst)
441 raise error.CommandError(None, inst)
433
442
434 if args:
443 if args:
435 cmd, args = args[0], args[1:]
444 cmd, args = args[0], args[1:]
436 aliases, entry = cmdutil.findcmd(cmd, commands.table,
445 aliases, entry = cmdutil.findcmd(cmd, commands.table,
437 ui.configbool("ui", "strict"))
446 ui.configbool("ui", "strict"))
438 cmd = aliases[0]
447 cmd = aliases[0]
439 args = aliasargs(entry[0], args)
448 args = aliasargs(entry[0], args)
440 defaults = ui.config("defaults", cmd)
449 defaults = ui.config("defaults", cmd)
441 if defaults:
450 if defaults:
442 args = map(util.expandpath, shlex.split(defaults)) + args
451 args = map(util.expandpath, shlex.split(defaults)) + args
443 c = list(entry[1])
452 c = list(entry[1])
444 else:
453 else:
445 cmd = None
454 cmd = None
446 c = []
455 c = []
447
456
448 # combine global options into local
457 # combine global options into local
449 for o in commands.globalopts:
458 for o in commands.globalopts:
450 c.append((o[0], o[1], options[o[1]], o[3]))
459 c.append((o[0], o[1], options[o[1]], o[3]))
451
460
452 try:
461 try:
453 args = fancyopts.fancyopts(args, c, cmdoptions, True)
462 args = fancyopts.fancyopts(args, c, cmdoptions, True)
454 except fancyopts.getopt.GetoptError, inst:
463 except fancyopts.getopt.GetoptError, inst:
455 raise error.CommandError(cmd, inst)
464 raise error.CommandError(cmd, inst)
456
465
457 # separate global options back out
466 # separate global options back out
458 for o in commands.globalopts:
467 for o in commands.globalopts:
459 n = o[1]
468 n = o[1]
460 options[n] = cmdoptions[n]
469 options[n] = cmdoptions[n]
461 del cmdoptions[n]
470 del cmdoptions[n]
462
471
463 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
472 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
464
473
465 def _parseconfig(ui, config):
474 def _parseconfig(ui, config):
466 """parse the --config options from the command line"""
475 """parse the --config options from the command line"""
467 configs = []
476 configs = []
468
477
469 for cfg in config:
478 for cfg in config:
470 try:
479 try:
471 name, value = cfg.split('=', 1)
480 name, value = cfg.split('=', 1)
472 section, name = name.split('.', 1)
481 section, name = name.split('.', 1)
473 if not section or not name:
482 if not section or not name:
474 raise IndexError
483 raise IndexError
475 ui.setconfig(section, name, value)
484 ui.setconfig(section, name, value)
476 configs.append((section, name, value))
485 configs.append((section, name, value))
477 except (IndexError, ValueError):
486 except (IndexError, ValueError):
478 raise util.Abort(_('malformed --config option: %r '
487 raise util.Abort(_('malformed --config option: %r '
479 '(use --config section.name=value)') % cfg)
488 '(use --config section.name=value)') % cfg)
480
489
481 return configs
490 return configs
482
491
483 def _earlygetopt(aliases, args):
492 def _earlygetopt(aliases, args):
484 """Return list of values for an option (or aliases).
493 """Return list of values for an option (or aliases).
485
494
486 The values are listed in the order they appear in args.
495 The values are listed in the order they appear in args.
487 The options and values are removed from args.
496 The options and values are removed from args.
488
497
489 >>> args = ['x', '--cwd', 'foo', 'y']
498 >>> args = ['x', '--cwd', 'foo', 'y']
490 >>> _earlygetopt(['--cwd'], args), args
499 >>> _earlygetopt(['--cwd'], args), args
491 (['foo'], ['x', 'y'])
500 (['foo'], ['x', 'y'])
492
501
493 >>> args = ['x', '--cwd=bar', 'y']
502 >>> args = ['x', '--cwd=bar', 'y']
494 >>> _earlygetopt(['--cwd'], args), args
503 >>> _earlygetopt(['--cwd'], args), args
495 (['bar'], ['x', 'y'])
504 (['bar'], ['x', 'y'])
496
505
497 >>> args = ['x', '-R', 'foo', 'y']
506 >>> args = ['x', '-R', 'foo', 'y']
498 >>> _earlygetopt(['-R'], args), args
507 >>> _earlygetopt(['-R'], args), args
499 (['foo'], ['x', 'y'])
508 (['foo'], ['x', 'y'])
500
509
501 >>> args = ['x', '-Rbar', 'y']
510 >>> args = ['x', '-Rbar', 'y']
502 >>> _earlygetopt(['-R'], args), args
511 >>> _earlygetopt(['-R'], args), args
503 (['bar'], ['x', 'y'])
512 (['bar'], ['x', 'y'])
504 """
513 """
505 try:
514 try:
506 argcount = args.index("--")
515 argcount = args.index("--")
507 except ValueError:
516 except ValueError:
508 argcount = len(args)
517 argcount = len(args)
509 shortopts = [opt for opt in aliases if len(opt) == 2]
518 shortopts = [opt for opt in aliases if len(opt) == 2]
510 values = []
519 values = []
511 pos = 0
520 pos = 0
512 while pos < argcount:
521 while pos < argcount:
513 fullarg = arg = args[pos]
522 fullarg = arg = args[pos]
514 equals = arg.find('=')
523 equals = arg.find('=')
515 if equals > -1:
524 if equals > -1:
516 arg = arg[:equals]
525 arg = arg[:equals]
517 if arg in aliases:
526 if arg in aliases:
518 del args[pos]
527 del args[pos]
519 if equals > -1:
528 if equals > -1:
520 values.append(fullarg[equals + 1:])
529 values.append(fullarg[equals + 1:])
521 argcount -= 1
530 argcount -= 1
522 else:
531 else:
523 if pos + 1 >= argcount:
532 if pos + 1 >= argcount:
524 # ignore and let getopt report an error if there is no value
533 # ignore and let getopt report an error if there is no value
525 break
534 break
526 values.append(args.pop(pos))
535 values.append(args.pop(pos))
527 argcount -= 2
536 argcount -= 2
528 elif arg[:2] in shortopts:
537 elif arg[:2] in shortopts:
529 # short option can have no following space, e.g. hg log -Rfoo
538 # short option can have no following space, e.g. hg log -Rfoo
530 values.append(args.pop(pos)[2:])
539 values.append(args.pop(pos)[2:])
531 argcount -= 1
540 argcount -= 1
532 else:
541 else:
533 pos += 1
542 pos += 1
534 return values
543 return values
535
544
536 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
545 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
537 # run pre-hook, and abort if it fails
546 # run pre-hook, and abort if it fails
538 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
547 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
539 pats=cmdpats, opts=cmdoptions)
548 pats=cmdpats, opts=cmdoptions)
540 ret = _runcommand(ui, options, cmd, d)
549 ret = _runcommand(ui, options, cmd, d)
541 # run post-hook, passing command result
550 # run post-hook, passing command result
542 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
551 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
543 result=ret, pats=cmdpats, opts=cmdoptions)
552 result=ret, pats=cmdpats, opts=cmdoptions)
544 return ret
553 return ret
545
554
546 def _getlocal(ui, rpath):
555 def _getlocal(ui, rpath):
547 """Return (path, local ui object) for the given target path.
556 """Return (path, local ui object) for the given target path.
548
557
549 Takes paths in [cwd]/.hg/hgrc into account."
558 Takes paths in [cwd]/.hg/hgrc into account."
550 """
559 """
551 try:
560 try:
552 wd = os.getcwd()
561 wd = os.getcwd()
553 except OSError, e:
562 except OSError, e:
554 raise util.Abort(_("error getting current working directory: %s") %
563 raise util.Abort(_("error getting current working directory: %s") %
555 e.strerror)
564 e.strerror)
556 path = cmdutil.findrepo(wd) or ""
565 path = cmdutil.findrepo(wd) or ""
557 if not path:
566 if not path:
558 lui = ui
567 lui = ui
559 else:
568 else:
560 lui = ui.copy()
569 lui = ui.copy()
561 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
570 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
562
571
563 if rpath and rpath[-1]:
572 if rpath and rpath[-1]:
564 path = lui.expandpath(rpath[-1])
573 path = lui.expandpath(rpath[-1])
565 lui = ui.copy()
574 lui = ui.copy()
566 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
575 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
567
576
568 return path, lui
577 return path, lui
569
578
570 def _checkshellalias(lui, ui, args):
579 def _checkshellalias(lui, ui, args):
571 options = {}
580 options = {}
572
581
573 try:
582 try:
574 args = fancyopts.fancyopts(args, commands.globalopts, options)
583 args = fancyopts.fancyopts(args, commands.globalopts, options)
575 except fancyopts.getopt.GetoptError:
584 except fancyopts.getopt.GetoptError:
576 return
585 return
577
586
578 if not args:
587 if not args:
579 return
588 return
580
589
581 norepo = commands.norepo
590 norepo = commands.norepo
582 optionalrepo = commands.optionalrepo
591 optionalrepo = commands.optionalrepo
583 def restorecommands():
592 def restorecommands():
584 commands.norepo = norepo
593 commands.norepo = norepo
585 commands.optionalrepo = optionalrepo
594 commands.optionalrepo = optionalrepo
586
595
587 cmdtable = commands.table.copy()
596 cmdtable = commands.table.copy()
588 addaliases(lui, cmdtable)
597 addaliases(lui, cmdtable)
589
598
590 cmd = args[0]
599 cmd = args[0]
591 try:
600 try:
592 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
601 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
593 lui.configbool("ui", "strict"))
602 lui.configbool("ui", "strict"))
594 except (error.AmbiguousCommand, error.UnknownCommand):
603 except (error.AmbiguousCommand, error.UnknownCommand):
595 restorecommands()
604 restorecommands()
596 return
605 return
597
606
598 cmd = aliases[0]
607 cmd = aliases[0]
599 fn = entry[0]
608 fn = entry[0]
600
609
601 if cmd and util.safehasattr(fn, 'shell'):
610 if cmd and util.safehasattr(fn, 'shell'):
602 d = lambda: fn(ui, *args[1:])
611 d = lambda: fn(ui, *args[1:])
603 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
612 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
604 [], {})
613 [], {})
605
614
606 restorecommands()
615 restorecommands()
607
616
608 _loaded = set()
617 _loaded = set()
609 def _dispatch(req):
618 def _dispatch(req):
610 args = req.args
619 args = req.args
611 ui = req.ui
620 ui = req.ui
612
621
613 # read --config before doing anything else
622 # read --config before doing anything else
614 # (e.g. to change trust settings for reading .hg/hgrc)
623 # (e.g. to change trust settings for reading .hg/hgrc)
615 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
624 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
616
625
617 # check for cwd
626 # check for cwd
618 cwd = _earlygetopt(['--cwd'], args)
627 cwd = _earlygetopt(['--cwd'], args)
619 if cwd:
628 if cwd:
620 os.chdir(cwd[-1])
629 os.chdir(cwd[-1])
621
630
622 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
631 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
623 path, lui = _getlocal(ui, rpath)
632 path, lui = _getlocal(ui, rpath)
624
633
625 # Now that we're operating in the right directory/repository with
634 # Now that we're operating in the right directory/repository with
626 # the right config settings, check for shell aliases
635 # the right config settings, check for shell aliases
627 shellaliasfn = _checkshellalias(lui, ui, args)
636 shellaliasfn = _checkshellalias(lui, ui, args)
628 if shellaliasfn:
637 if shellaliasfn:
629 return shellaliasfn()
638 return shellaliasfn()
630
639
631 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
640 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
632 # reposetup. Programs like TortoiseHg will call _dispatch several
641 # reposetup. Programs like TortoiseHg will call _dispatch several
633 # times so we keep track of configured extensions in _loaded.
642 # times so we keep track of configured extensions in _loaded.
634 extensions.loadall(lui)
643 extensions.loadall(lui)
635 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
644 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
636 # Propagate any changes to lui.__class__ by extensions
645 # Propagate any changes to lui.__class__ by extensions
637 ui.__class__ = lui.__class__
646 ui.__class__ = lui.__class__
638
647
639 # (uisetup and extsetup are handled in extensions.loadall)
648 # (uisetup and extsetup are handled in extensions.loadall)
640
649
641 for name, module in exts:
650 for name, module in exts:
642 cmdtable = getattr(module, 'cmdtable', {})
651 cmdtable = getattr(module, 'cmdtable', {})
643 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
652 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
644 if overrides:
653 if overrides:
645 ui.warn(_("extension '%s' overrides commands: %s\n")
654 ui.warn(_("extension '%s' overrides commands: %s\n")
646 % (name, " ".join(overrides)))
655 % (name, " ".join(overrides)))
647 commands.table.update(cmdtable)
656 commands.table.update(cmdtable)
648 _loaded.add(name)
657 _loaded.add(name)
649
658
650 # (reposetup is handled in hg.repository)
659 # (reposetup is handled in hg.repository)
651
660
652 addaliases(lui, commands.table)
661 addaliases(lui, commands.table)
653
662
654 # check for fallback encoding
663 # check for fallback encoding
655 fallback = lui.config('ui', 'fallbackencoding')
664 fallback = lui.config('ui', 'fallbackencoding')
656 if fallback:
665 if fallback:
657 encoding.fallbackencoding = fallback
666 encoding.fallbackencoding = fallback
658
667
659 fullargs = args
668 fullargs = args
660 cmd, func, args, options, cmdoptions = _parse(lui, args)
669 cmd, func, args, options, cmdoptions = _parse(lui, args)
661
670
662 if options["config"]:
671 if options["config"]:
663 raise util.Abort(_("option --config may not be abbreviated!"))
672 raise util.Abort(_("option --config may not be abbreviated!"))
664 if options["cwd"]:
673 if options["cwd"]:
665 raise util.Abort(_("option --cwd may not be abbreviated!"))
674 raise util.Abort(_("option --cwd may not be abbreviated!"))
666 if options["repository"]:
675 if options["repository"]:
667 raise util.Abort(_(
676 raise util.Abort(_(
668 "option -R has to be separated from other options (e.g. not -qR) "
677 "option -R has to be separated from other options (e.g. not -qR) "
669 "and --repository may only be abbreviated as --repo!"))
678 "and --repository may only be abbreviated as --repo!"))
670
679
671 if options["encoding"]:
680 if options["encoding"]:
672 encoding.encoding = options["encoding"]
681 encoding.encoding = options["encoding"]
673 if options["encodingmode"]:
682 if options["encodingmode"]:
674 encoding.encodingmode = options["encodingmode"]
683 encoding.encodingmode = options["encodingmode"]
675 if options["time"]:
684 if options["time"]:
676 def get_times():
685 def get_times():
677 t = os.times()
686 t = os.times()
678 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
687 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
679 t = (t[0], t[1], t[2], t[3], time.clock())
688 t = (t[0], t[1], t[2], t[3], time.clock())
680 return t
689 return t
681 s = get_times()
690 s = get_times()
682 def print_time():
691 def print_time():
683 t = get_times()
692 t = get_times()
684 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
693 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
685 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
694 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
686 atexit.register(print_time)
695 atexit.register(print_time)
687
696
688 uis = set([ui, lui])
697 uis = set([ui, lui])
689
698
690 if req.repo:
699 if req.repo:
691 uis.add(req.repo.ui)
700 uis.add(req.repo.ui)
692
701
693 # copy configs that were passed on the cmdline (--config) to the repo ui
702 # copy configs that were passed on the cmdline (--config) to the repo ui
694 for cfg in cfgs:
703 for cfg in cfgs:
695 req.repo.ui.setconfig(*cfg)
704 req.repo.ui.setconfig(*cfg)
696
705
697 if options['verbose'] or options['debug'] or options['quiet']:
706 if options['verbose'] or options['debug'] or options['quiet']:
698 for opt in ('verbose', 'debug', 'quiet'):
707 for opt in ('verbose', 'debug', 'quiet'):
699 val = str(bool(options[opt]))
708 val = str(bool(options[opt]))
700 for ui_ in uis:
709 for ui_ in uis:
701 ui_.setconfig('ui', opt, val)
710 ui_.setconfig('ui', opt, val)
702
711
703 if options['traceback']:
712 if options['traceback']:
704 for ui_ in uis:
713 for ui_ in uis:
705 ui_.setconfig('ui', 'traceback', 'on')
714 ui_.setconfig('ui', 'traceback', 'on')
706
715
707 if options['noninteractive']:
716 if options['noninteractive']:
708 for ui_ in uis:
717 for ui_ in uis:
709 ui_.setconfig('ui', 'interactive', 'off')
718 ui_.setconfig('ui', 'interactive', 'off')
710
719
711 if cmdoptions.get('insecure', False):
720 if cmdoptions.get('insecure', False):
712 for ui_ in uis:
721 for ui_ in uis:
713 ui_.setconfig('web', 'cacerts', '')
722 ui_.setconfig('web', 'cacerts', '')
714
723
715 if options['version']:
724 if options['version']:
716 return commands.version_(ui)
725 return commands.version_(ui)
717 if options['help']:
726 if options['help']:
718 return commands.help_(ui, cmd)
727 return commands.help_(ui, cmd)
719 elif not cmd:
728 elif not cmd:
720 return commands.help_(ui, 'shortlist')
729 return commands.help_(ui, 'shortlist')
721
730
722 repo = None
731 repo = None
723 cmdpats = args[:]
732 cmdpats = args[:]
724 if cmd not in commands.norepo.split():
733 if cmd not in commands.norepo.split():
725 # use the repo from the request only if we don't have -R
734 # use the repo from the request only if we don't have -R
726 if not rpath and not cwd:
735 if not rpath and not cwd:
727 repo = req.repo
736 repo = req.repo
728
737
729 if repo:
738 if repo:
730 # set the descriptors of the repo ui to those of ui
739 # set the descriptors of the repo ui to those of ui
731 repo.ui.fin = ui.fin
740 repo.ui.fin = ui.fin
732 repo.ui.fout = ui.fout
741 repo.ui.fout = ui.fout
733 repo.ui.ferr = ui.ferr
742 repo.ui.ferr = ui.ferr
734 else:
743 else:
735 try:
744 try:
736 repo = hg.repository(ui, path=path)
745 repo = hg.repository(ui, path=path)
737 if not repo.local():
746 if not repo.local():
738 raise util.Abort(_("repository '%s' is not local") % path)
747 raise util.Abort(_("repository '%s' is not local") % path)
739 if options['hidden']:
748 if options['hidden']:
740 repo = repo.unfiltered()
749 repo = repo.unfiltered()
741 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
750 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
742 except error.RequirementError:
751 except error.RequirementError:
743 raise
752 raise
744 except error.RepoError:
753 except error.RepoError:
745 if cmd not in commands.optionalrepo.split():
754 if cmd not in commands.optionalrepo.split():
746 if (cmd in commands.inferrepo.split() and
755 if (cmd in commands.inferrepo.split() and
747 args and not path): # try to infer -R from command args
756 args and not path): # try to infer -R from command args
748 repos = map(cmdutil.findrepo, args)
757 repos = map(cmdutil.findrepo, args)
749 guess = repos[0]
758 guess = repos[0]
750 if guess and repos.count(guess) == len(repos):
759 if guess and repos.count(guess) == len(repos):
751 req.args = ['--repository', guess] + fullargs
760 req.args = ['--repository', guess] + fullargs
752 return _dispatch(req)
761 return _dispatch(req)
753 if not path:
762 if not path:
754 raise error.RepoError(_("no repository found in '%s'"
763 raise error.RepoError(_("no repository found in '%s'"
755 " (.hg not found)")
764 " (.hg not found)")
756 % os.getcwd())
765 % os.getcwd())
757 raise
766 raise
758 if repo:
767 if repo:
759 ui = repo.ui
768 ui = repo.ui
760 args.insert(0, repo)
769 args.insert(0, repo)
761 elif rpath:
770 elif rpath:
762 ui.warn(_("warning: --repository ignored\n"))
771 ui.warn(_("warning: --repository ignored\n"))
763
772
764 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
773 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
765 ui.log("command", '%s\n', msg)
774 ui.log("command", '%s\n', msg)
766 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
775 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
767 starttime = time.time()
768 ret = None
769 try:
776 try:
770 ret = runcommand(lui, repo, cmd, fullargs, ui, options, d,
777 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
771 cmdpats, cmdoptions)
778 cmdpats, cmdoptions)
772 return ret
773 finally:
779 finally:
774 duration = time.time() - starttime
775 ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
776 cmd, ret, duration)
777 if repo and repo != req.repo:
780 if repo and repo != req.repo:
778 repo.close()
781 repo.close()
779
782
780 def lsprofile(ui, func, fp):
783 def lsprofile(ui, func, fp):
781 format = ui.config('profiling', 'format', default='text')
784 format = ui.config('profiling', 'format', default='text')
782 field = ui.config('profiling', 'sort', default='inlinetime')
785 field = ui.config('profiling', 'sort', default='inlinetime')
783 limit = ui.configint('profiling', 'limit', default=30)
786 limit = ui.configint('profiling', 'limit', default=30)
784 climit = ui.configint('profiling', 'nested', default=5)
787 climit = ui.configint('profiling', 'nested', default=5)
785
788
786 if format not in ['text', 'kcachegrind']:
789 if format not in ['text', 'kcachegrind']:
787 ui.warn(_("unrecognized profiling format '%s'"
790 ui.warn(_("unrecognized profiling format '%s'"
788 " - Ignored\n") % format)
791 " - Ignored\n") % format)
789 format = 'text'
792 format = 'text'
790
793
791 try:
794 try:
792 from mercurial import lsprof
795 from mercurial import lsprof
793 except ImportError:
796 except ImportError:
794 raise util.Abort(_(
797 raise util.Abort(_(
795 'lsprof not available - install from '
798 'lsprof not available - install from '
796 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
799 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
797 p = lsprof.Profiler()
800 p = lsprof.Profiler()
798 p.enable(subcalls=True)
801 p.enable(subcalls=True)
799 try:
802 try:
800 return func()
803 return func()
801 finally:
804 finally:
802 p.disable()
805 p.disable()
803
806
804 if format == 'kcachegrind':
807 if format == 'kcachegrind':
805 import lsprofcalltree
808 import lsprofcalltree
806 calltree = lsprofcalltree.KCacheGrind(p)
809 calltree = lsprofcalltree.KCacheGrind(p)
807 calltree.output(fp)
810 calltree.output(fp)
808 else:
811 else:
809 # format == 'text'
812 # format == 'text'
810 stats = lsprof.Stats(p.getstats())
813 stats = lsprof.Stats(p.getstats())
811 stats.sort(field)
814 stats.sort(field)
812 stats.pprint(limit=limit, file=fp, climit=climit)
815 stats.pprint(limit=limit, file=fp, climit=climit)
813
816
814 def statprofile(ui, func, fp):
817 def statprofile(ui, func, fp):
815 try:
818 try:
816 import statprof
819 import statprof
817 except ImportError:
820 except ImportError:
818 raise util.Abort(_(
821 raise util.Abort(_(
819 'statprof not available - install using "easy_install statprof"'))
822 'statprof not available - install using "easy_install statprof"'))
820
823
821 freq = ui.configint('profiling', 'freq', default=1000)
824 freq = ui.configint('profiling', 'freq', default=1000)
822 if freq > 0:
825 if freq > 0:
823 statprof.reset(freq)
826 statprof.reset(freq)
824 else:
827 else:
825 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
828 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
826
829
827 statprof.start()
830 statprof.start()
828 try:
831 try:
829 return func()
832 return func()
830 finally:
833 finally:
831 statprof.stop()
834 statprof.stop()
832 statprof.display(fp)
835 statprof.display(fp)
833
836
834 def _runcommand(ui, options, cmd, cmdfunc):
837 def _runcommand(ui, options, cmd, cmdfunc):
835 def checkargs():
838 def checkargs():
836 try:
839 try:
837 return cmdfunc()
840 return cmdfunc()
838 except error.SignatureError:
841 except error.SignatureError:
839 raise error.CommandError(cmd, _("invalid arguments"))
842 raise error.CommandError(cmd, _("invalid arguments"))
840
843
841 if options['profile']:
844 if options['profile']:
842 profiler = os.getenv('HGPROF')
845 profiler = os.getenv('HGPROF')
843 if profiler is None:
846 if profiler is None:
844 profiler = ui.config('profiling', 'type', default='ls')
847 profiler = ui.config('profiling', 'type', default='ls')
845 if profiler not in ('ls', 'stat'):
848 if profiler not in ('ls', 'stat'):
846 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
849 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
847 profiler = 'ls'
850 profiler = 'ls'
848
851
849 output = ui.config('profiling', 'output')
852 output = ui.config('profiling', 'output')
850
853
851 if output:
854 if output:
852 path = ui.expandpath(output)
855 path = ui.expandpath(output)
853 fp = open(path, 'wb')
856 fp = open(path, 'wb')
854 else:
857 else:
855 fp = sys.stderr
858 fp = sys.stderr
856
859
857 try:
860 try:
858 if profiler == 'ls':
861 if profiler == 'ls':
859 return lsprofile(ui, checkargs, fp)
862 return lsprofile(ui, checkargs, fp)
860 else:
863 else:
861 return statprofile(ui, checkargs, fp)
864 return statprofile(ui, checkargs, fp)
862 finally:
865 finally:
863 if output:
866 if output:
864 fp.close()
867 fp.close()
865 else:
868 else:
866 return checkargs()
869 return checkargs()
@@ -1,154 +1,154 b''
1 setup
1 setup
2 $ cat > mock.py <<EOF
2 $ cat > mock.py <<EOF
3 > from mercurial import util
3 > from mercurial import util
4 >
4 >
5 > def makedate():
5 > def makedate():
6 > return 0, 0
6 > return 0, 0
7 > def getuser():
7 > def getuser():
8 > return 'bob'
8 > return 'bob'
9 > # mock the date and user apis so the output is always the same
9 > # mock the date and user apis so the output is always the same
10 > def uisetup(ui):
10 > def uisetup(ui):
11 > util.makedate = makedate
11 > util.makedate = makedate
12 > util.getuser = getuser
12 > util.getuser = getuser
13 > EOF
13 > EOF
14 $ cat >> $HGRCPATH <<EOF
14 $ cat >> $HGRCPATH <<EOF
15 > [extensions]
15 > [extensions]
16 > blackbox=
16 > blackbox=
17 > mock=`pwd`/mock.py
17 > mock=`pwd`/mock.py
18 > mq=
18 > mq=
19 > EOF
19 > EOF
20 $ hg init blackboxtest
20 $ hg init blackboxtest
21 $ cd blackboxtest
21 $ cd blackboxtest
22
22
23 command, exit codes, and duration
23 command, exit codes, and duration
24
24
25 $ echo a > a
25 $ echo a > a
26 $ hg add a
26 $ hg add a
27 $ hg blackbox
27 $ hg blackbox
28 1970/01/01 00:00:00 bob> add a
28 1970/01/01 00:00:00 bob> add a
29 1970/01/01 00:00:00 bob> add exited 0 after * seconds (glob)
29 1970/01/01 00:00:00 bob> add a exited 0 after * seconds (glob)
30
30
31 incoming change tracking
31 incoming change tracking
32
32
33 create two heads to verify that we only see one change in the log later
33 create two heads to verify that we only see one change in the log later
34 $ hg commit -ma
34 $ hg commit -ma
35 $ hg up null
35 $ hg up null
36 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
36 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 $ echo b > b
37 $ echo b > b
38 $ hg commit -Amb
38 $ hg commit -Amb
39 adding b
39 adding b
40 created new head
40 created new head
41
41
42 clone, commit, pull
42 clone, commit, pull
43 $ hg clone . ../blackboxtest2
43 $ hg clone . ../blackboxtest2
44 updating to branch default
44 updating to branch default
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 $ echo c > c
46 $ echo c > c
47 $ hg commit -Amc
47 $ hg commit -Amc
48 adding c
48 adding c
49 $ cd ../blackboxtest2
49 $ cd ../blackboxtest2
50 $ hg pull
50 $ hg pull
51 pulling from $TESTTMP/blackboxtest (glob)
51 pulling from $TESTTMP/blackboxtest (glob)
52 searching for changes
52 searching for changes
53 adding changesets
53 adding changesets
54 adding manifests
54 adding manifests
55 adding file changes
55 adding file changes
56 added 1 changesets with 1 changes to 1 files
56 added 1 changesets with 1 changes to 1 files
57 (run 'hg update' to get a working copy)
57 (run 'hg update' to get a working copy)
58 $ hg blackbox -l 3
58 $ hg blackbox -l 3
59 1970/01/01 00:00:00 bob> pull
59 1970/01/01 00:00:00 bob> pull
60 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62
60 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62
61 1970/01/01 00:00:00 bob> pull exited None after * seconds (glob)
61 1970/01/01 00:00:00 bob> pull exited 0 after * seconds (glob)
62
62
63 we must not cause a failure if we cannot write to the log
63 we must not cause a failure if we cannot write to the log
64
64
65 $ hg rollback
65 $ hg rollback
66 repository tip rolled back to revision 1 (undo pull)
66 repository tip rolled back to revision 1 (undo pull)
67
67
68 #if unix-permissions
68 #if unix-permissions
69 $ chmod 000 .hg/blackbox.log
69 $ chmod 000 .hg/blackbox.log
70 $ hg --debug incoming
70 $ hg --debug incoming
71 warning: cannot write to blackbox.log: Permission denied
71 warning: cannot write to blackbox.log: Permission denied
72 comparing with $TESTTMP/blackboxtest (glob)
72 comparing with $TESTTMP/blackboxtest (glob)
73 query 1; heads
73 query 1; heads
74 searching for changes
74 searching for changes
75 all local heads known remotely
75 all local heads known remotely
76 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
76 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
77 tag: tip
77 tag: tip
78 phase: draft
78 phase: draft
79 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
79 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
80 parent: -1:0000000000000000000000000000000000000000
80 parent: -1:0000000000000000000000000000000000000000
81 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
81 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
82 user: test
82 user: test
83 date: Thu Jan 01 00:00:00 1970 +0000
83 date: Thu Jan 01 00:00:00 1970 +0000
84 files+: c
84 files+: c
85 extra: branch=default
85 extra: branch=default
86 description:
86 description:
87 c
87 c
88
88
89
89
90 #endif
90 #endif
91 $ hg pull
91 $ hg pull
92 pulling from $TESTTMP/blackboxtest (glob)
92 pulling from $TESTTMP/blackboxtest (glob)
93 searching for changes
93 searching for changes
94 adding changesets
94 adding changesets
95 adding manifests
95 adding manifests
96 adding file changes
96 adding file changes
97 added 1 changesets with 1 changes to 1 files
97 added 1 changesets with 1 changes to 1 files
98 (run 'hg update' to get a working copy)
98 (run 'hg update' to get a working copy)
99
99
100 a failure reading from the log is fine
100 a failure reading from the log is fine
101 #if unix-permissions
101 #if unix-permissions
102 $ hg blackbox -l 3
102 $ hg blackbox -l 3
103 abort: Permission denied: $TESTTMP/blackboxtest2/.hg/blackbox.log
103 abort: Permission denied: $TESTTMP/blackboxtest2/.hg/blackbox.log
104 [255]
104 [255]
105
105
106 $ chmod 600 .hg/blackbox.log
106 $ chmod 600 .hg/blackbox.log
107 #endif
107 #endif
108
108
109 backup bundles get logged
109 backup bundles get logged
110
110
111 $ touch d
111 $ touch d
112 $ hg commit -Amd
112 $ hg commit -Amd
113 adding d
113 adding d
114 created new head
114 created new head
115 $ hg strip tip
115 $ hg strip tip
116 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
116 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
117 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
117 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
118 $ hg blackbox -l 3
118 $ hg blackbox -l 3
119 1970/01/01 00:00:00 bob> strip tip
119 1970/01/01 00:00:00 bob> strip tip
120 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
120 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
121 1970/01/01 00:00:00 bob> strip exited 0 after * seconds (glob)
121 1970/01/01 00:00:00 bob> strip tip exited 0 after * seconds (glob)
122
122
123 extension and python hooks - use the eol extension for a pythonhook
123 extension and python hooks - use the eol extension for a pythonhook
124
124
125 $ echo '[extensions]' >> .hg/hgrc
125 $ echo '[extensions]' >> .hg/hgrc
126 $ echo 'eol=' >> .hg/hgrc
126 $ echo 'eol=' >> .hg/hgrc
127 $ echo '[hooks]' >> .hg/hgrc
127 $ echo '[hooks]' >> .hg/hgrc
128 $ echo 'update = echo hooked' >> .hg/hgrc
128 $ echo 'update = echo hooked' >> .hg/hgrc
129 $ hg update
129 $ hg update
130 hooked
130 hooked
131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 $ hg blackbox -l 4
132 $ hg blackbox -l 4
133 1970/01/01 00:00:00 bob> update
133 1970/01/01 00:00:00 bob> update
134 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
134 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
135 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob)
135 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob)
136 1970/01/01 00:00:00 bob> update exited False after * seconds (glob)
136 1970/01/01 00:00:00 bob> update exited 0 after * seconds (glob)
137
137
138 log rotation
138 log rotation
139
139
140 $ echo '[blackbox]' >> .hg/hgrc
140 $ echo '[blackbox]' >> .hg/hgrc
141 $ echo 'maxsize = 20 b' >> .hg/hgrc
141 $ echo 'maxsize = 20 b' >> .hg/hgrc
142 $ echo 'maxfiles = 3' >> .hg/hgrc
142 $ echo 'maxfiles = 3' >> .hg/hgrc
143 $ hg status
143 $ hg status
144 $ hg status
144 $ hg status
145 $ hg status
145 $ hg status
146 $ hg tip -q
146 $ hg tip -q
147 2:d02f48003e62
147 2:d02f48003e62
148 $ ls .hg/blackbox.log*
148 $ ls .hg/blackbox.log*
149 .hg/blackbox.log
149 .hg/blackbox.log
150 .hg/blackbox.log.1
150 .hg/blackbox.log.1
151 .hg/blackbox.log.2
151 .hg/blackbox.log.2
152
152
153 cleanup
153 cleanup
154 $ cd ..
154 $ cd ..
@@ -1,210 +1,208 b''
1
1
2 $ cat > loop.py <<EOF
2 $ cat > loop.py <<EOF
3 > from mercurial import commands
3 > from mercurial import commands
4 >
4 >
5 > def loop(ui, loops, **opts):
5 > def loop(ui, loops, **opts):
6 > loops = int(loops)
6 > loops = int(loops)
7 > total = None
7 > total = None
8 > if loops >= 0:
8 > if loops >= 0:
9 > total = loops
9 > total = loops
10 > if opts.get('total', None):
10 > if opts.get('total', None):
11 > total = int(opts.get('total'))
11 > total = int(opts.get('total'))
12 > nested = False
12 > nested = False
13 > if opts.get('nested', None):
13 > if opts.get('nested', None):
14 > nested = True
14 > nested = True
15 > loops = abs(loops)
15 > loops = abs(loops)
16 >
16 >
17 > for i in range(loops):
17 > for i in range(loops):
18 > ui.progress('loop', i, 'loop.%d' % i, 'loopnum', total)
18 > ui.progress('loop', i, 'loop.%d' % i, 'loopnum', total)
19 > if opts.get('parallel'):
19 > if opts.get('parallel'):
20 > ui.progress('other', i, 'other.%d' % i, 'othernum', total)
20 > ui.progress('other', i, 'other.%d' % i, 'othernum', total)
21 > if nested:
21 > if nested:
22 > for j in range(2):
22 > for j in range(2):
23 > ui.progress('nested', j, 'nested.%d' % j, 'nestnum', 2)
23 > ui.progress('nested', j, 'nested.%d' % j, 'nestnum', 2)
24 > ui.progress('nested', None, 'nested.done', 'nestnum', 2)
24 > ui.progress('nested', None, 'nested.done', 'nestnum', 2)
25 > ui.progress('loop', None, 'loop.done', 'loopnum', total)
25 > ui.progress('loop', None, 'loop.done', 'loopnum', total)
26 >
26 >
27 > commands.norepo += " loop"
27 > commands.norepo += " loop"
28 >
28 >
29 > cmdtable = {
29 > cmdtable = {
30 > "loop": (loop, [('', 'total', '', 'override for total'),
30 > "loop": (loop, [('', 'total', '', 'override for total'),
31 > ('', 'nested', False, 'show nested results'),
31 > ('', 'nested', False, 'show nested results'),
32 > ('', 'parallel', False, 'show parallel sets of results'),
32 > ('', 'parallel', False, 'show parallel sets of results'),
33 > ],
33 > ],
34 > 'hg loop LOOPS'),
34 > 'hg loop LOOPS'),
35 > }
35 > }
36 > EOF
36 > EOF
37
37
38 $ cp $HGRCPATH $HGRCPATH.orig
38 $ cp $HGRCPATH $HGRCPATH.orig
39 $ echo "[extensions]" >> $HGRCPATH
39 $ echo "[extensions]" >> $HGRCPATH
40 $ echo "progress=" >> $HGRCPATH
40 $ echo "progress=" >> $HGRCPATH
41 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
41 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
42 $ echo "[progress]" >> $HGRCPATH
42 $ echo "[progress]" >> $HGRCPATH
43 $ echo "format = topic bar number" >> $HGRCPATH
43 $ echo "format = topic bar number" >> $HGRCPATH
44 $ echo "assume-tty=1" >> $HGRCPATH
44 $ echo "assume-tty=1" >> $HGRCPATH
45 $ echo "width=60" >> $HGRCPATH
45 $ echo "width=60" >> $HGRCPATH
46
46
47 test default params, display nothing because of delay
47 test default params, display nothing because of delay
48
48
49 $ hg -y loop 3
49 $ hg -y loop 3
50 $ echo "delay=0" >> $HGRCPATH
50 $ echo "delay=0" >> $HGRCPATH
51 $ echo "refresh=0" >> $HGRCPATH
51 $ echo "refresh=0" >> $HGRCPATH
52
52
53 test with delay=0, refresh=0
53 test with delay=0, refresh=0
54
54
55 $ hg -y loop 3
55 $ hg -y loop 3
56 \r (no-eol) (esc)
56 \r (no-eol) (esc)
57 loop [ ] 0/3\r (no-eol) (esc)
57 loop [ ] 0/3\r (no-eol) (esc)
58 loop [===============> ] 1/3\r (no-eol) (esc)
58 loop [===============> ] 1/3\r (no-eol) (esc)
59 loop [===============================> ] 2/3\r (no-eol) (esc)
59 loop [===============================> ] 2/3\r (no-eol) (esc)
60 \r (no-eol) (esc)
60 \r (no-eol) (esc)
61
61
62
62
63 test nested short-lived topics (which shouldn't display with nestdelay):
63 test nested short-lived topics (which shouldn't display with nestdelay):
64
64
65 $ hg -y loop 3 --nested
65 $ hg -y loop 3 --nested
66 \r (no-eol) (esc)
66 \r (no-eol) (esc)
67 loop [ ] 0/3\r (no-eol) (esc)
67 loop [ ] 0/3\r (no-eol) (esc)
68 loop [===============> ] 1/3\r (no-eol) (esc)
68 loop [===============> ] 1/3\r (no-eol) (esc)
69 loop [===============================> ] 2/3\r (no-eol) (esc)
69 loop [===============================> ] 2/3\r (no-eol) (esc)
70 \r (no-eol) (esc)
70 \r (no-eol) (esc)
71
71
72
72
73 $ hg --config progress.changedelay=0 -y loop 3 --nested
73 $ hg --config progress.changedelay=0 -y loop 3 --nested
74 \r (no-eol) (esc)
74 \r (no-eol) (esc)
75 loop [ ] 0/3\r (no-eol) (esc)
75 loop [ ] 0/3\r (no-eol) (esc)
76 nested [ ] 0/2\r (no-eol) (esc)
76 nested [ ] 0/2\r (no-eol) (esc)
77 nested [======================> ] 1/2\r (no-eol) (esc)
77 nested [======================> ] 1/2\r (no-eol) (esc)
78 loop [===============> ] 1/3\r (no-eol) (esc)
78 loop [===============> ] 1/3\r (no-eol) (esc)
79 nested [ ] 0/2\r (no-eol) (esc)
79 nested [ ] 0/2\r (no-eol) (esc)
80 nested [======================> ] 1/2\r (no-eol) (esc)
80 nested [======================> ] 1/2\r (no-eol) (esc)
81 loop [===============================> ] 2/3\r (no-eol) (esc)
81 loop [===============================> ] 2/3\r (no-eol) (esc)
82 nested [ ] 0/2\r (no-eol) (esc)
82 nested [ ] 0/2\r (no-eol) (esc)
83 nested [======================> ] 1/2\r (no-eol) (esc)
83 nested [======================> ] 1/2\r (no-eol) (esc)
84 \r (no-eol) (esc)
84 \r (no-eol) (esc)
85
85
86
86
87 test two topics being printed in parallel (as when we're doing a local
87 test two topics being printed in parallel (as when we're doing a local
88 --pull clone, where you get the unbundle and bundle progress at the
88 --pull clone, where you get the unbundle and bundle progress at the
89 same time):
89 same time):
90 $ hg loop 3 --parallel
90 $ hg loop 3 --parallel
91 \r (no-eol) (esc)
91 \r (no-eol) (esc)
92 loop [ ] 0/3\r (no-eol) (esc)
92 loop [ ] 0/3\r (no-eol) (esc)
93 loop [===============> ] 1/3\r (no-eol) (esc)
93 loop [===============> ] 1/3\r (no-eol) (esc)
94 loop [===============================> ] 2/3\r (no-eol) (esc)
94 loop [===============================> ] 2/3\r (no-eol) (esc)
95 \r (no-eol) (esc)
95 \r (no-eol) (esc)
96 test refresh is taken in account
96 test refresh is taken in account
97
97
98 $ hg -y --config progress.refresh=100 loop 3
98 $ hg -y --config progress.refresh=100 loop 3
99
99
100 test format options 1
100 test format options 1
101
101
102 $ hg -y --config 'progress.format=number topic item+2' loop 2
102 $ hg -y --config 'progress.format=number topic item+2' loop 2
103 \r (no-eol) (esc)
103 \r (no-eol) (esc)
104 0/2 loop lo\r (no-eol) (esc)
104 0/2 loop lo\r (no-eol) (esc)
105 1/2 loop lo\r (no-eol) (esc)
105 1/2 loop lo\r (no-eol) (esc)
106 \r (no-eol) (esc)
106 \r (no-eol) (esc)
107
107
108 test format options 2
108 test format options 2
109
109
110 $ hg -y --config 'progress.format=number item-3 bar' loop 2
110 $ hg -y --config 'progress.format=number item-3 bar' loop 2
111 \r (no-eol) (esc)
111 \r (no-eol) (esc)
112 0/2 p.0 [ ]\r (no-eol) (esc)
112 0/2 p.0 [ ]\r (no-eol) (esc)
113 1/2 p.1 [=======================> ]\r (no-eol) (esc)
113 1/2 p.1 [=======================> ]\r (no-eol) (esc)
114 \r (no-eol) (esc)
114 \r (no-eol) (esc)
115
115
116 test format options and indeterminate progress
116 test format options and indeterminate progress
117
117
118 $ hg -y --config 'progress.format=number item bar' loop -- -2
118 $ hg -y --config 'progress.format=number item bar' loop -- -2
119 \r (no-eol) (esc)
119 \r (no-eol) (esc)
120 0 loop.0 [ <=> ]\r (no-eol) (esc)
120 0 loop.0 [ <=> ]\r (no-eol) (esc)
121 1 loop.1 [ <=> ]\r (no-eol) (esc)
121 1 loop.1 [ <=> ]\r (no-eol) (esc)
122 \r (no-eol) (esc)
122 \r (no-eol) (esc)
123
123
124 make sure things don't fall over if count > total
124 make sure things don't fall over if count > total
125
125
126 $ hg -y loop --total 4 6
126 $ hg -y loop --total 4 6
127 \r (no-eol) (esc)
127 \r (no-eol) (esc)
128 loop [ ] 0/4\r (no-eol) (esc)
128 loop [ ] 0/4\r (no-eol) (esc)
129 loop [===========> ] 1/4\r (no-eol) (esc)
129 loop [===========> ] 1/4\r (no-eol) (esc)
130 loop [=======================> ] 2/4\r (no-eol) (esc)
130 loop [=======================> ] 2/4\r (no-eol) (esc)
131 loop [===================================> ] 3/4\r (no-eol) (esc)
131 loop [===================================> ] 3/4\r (no-eol) (esc)
132 loop [===============================================>] 4/4\r (no-eol) (esc)
132 loop [===============================================>] 4/4\r (no-eol) (esc)
133 loop [ <=> ] 5/4\r (no-eol) (esc)
133 loop [ <=> ] 5/4\r (no-eol) (esc)
134 \r (no-eol) (esc)
134 \r (no-eol) (esc)
135
135
136 test immediate progress completion
136 test immediate progress completion
137
137
138 $ hg -y loop 0
138 $ hg -y loop 0
139
139
140 test delay time estimates
140 test delay time estimates
141
141
142 $ cat > mocktime.py <<EOF
142 $ cat > mocktime.py <<EOF
143 > import os
143 > import os
144 > import time
144 > import time
145 >
145 >
146 > class mocktime(object):
146 > class mocktime(object):
147 > def __init__(self, increment):
147 > def __init__(self, increment):
148 > self.time = 0
148 > self.time = 0
149 > self.increment = increment
149 > self.increment = increment
150 > def __call__(self):
150 > def __call__(self):
151 > self.time += self.increment
151 > self.time += self.increment
152 > return self.time
152 > return self.time
153 >
153 >
154 > def uisetup(ui):
154 > def uisetup(ui):
155 > time.time = mocktime(int(os.environ.get('MOCKTIME', '11')))
155 > time.time = mocktime(int(os.environ.get('MOCKTIME', '11')))
156 > EOF
156 > EOF
157
157
158 $ cp $HGRCPATH.orig $HGRCPATH
158 $ cp $HGRCPATH.orig $HGRCPATH
159 $ echo "[extensions]" >> $HGRCPATH
159 $ echo "[extensions]" >> $HGRCPATH
160 $ echo "mocktime=`pwd`/mocktime.py" >> $HGRCPATH
160 $ echo "mocktime=`pwd`/mocktime.py" >> $HGRCPATH
161 $ echo "progress=" >> $HGRCPATH
161 $ echo "progress=" >> $HGRCPATH
162 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
162 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
163 $ echo "[progress]" >> $HGRCPATH
163 $ echo "[progress]" >> $HGRCPATH
164 $ echo "assume-tty=1" >> $HGRCPATH
164 $ echo "assume-tty=1" >> $HGRCPATH
165 $ echo "delay=25" >> $HGRCPATH
165 $ echo "delay=25" >> $HGRCPATH
166 $ echo "width=60" >> $HGRCPATH
166 $ echo "width=60" >> $HGRCPATH
167
167
168 $ hg -y loop 8
168 $ hg -y loop 8
169 \r (no-eol) (esc)
169 \r (no-eol) (esc)
170 loop [====> ] 1/8 1m18s\r (no-eol) (esc)
171 loop [=========> ] 2/8 1m07s\r (no-eol) (esc)
170 loop [=========> ] 2/8 1m07s\r (no-eol) (esc)
172 loop [===============> ] 3/8 56s\r (no-eol) (esc)
171 loop [===============> ] 3/8 56s\r (no-eol) (esc)
173 loop [=====================> ] 4/8 45s\r (no-eol) (esc)
172 loop [=====================> ] 4/8 45s\r (no-eol) (esc)
174 loop [==========================> ] 5/8 34s\r (no-eol) (esc)
173 loop [==========================> ] 5/8 34s\r (no-eol) (esc)
175 loop [================================> ] 6/8 23s\r (no-eol) (esc)
174 loop [================================> ] 6/8 23s\r (no-eol) (esc)
176 loop [=====================================> ] 7/8 12s\r (no-eol) (esc)
175 loop [=====================================> ] 7/8 12s\r (no-eol) (esc)
177 \r (no-eol) (esc)
176 \r (no-eol) (esc)
178
177
179 $ MOCKTIME=10000 hg -y loop 4
178 $ MOCKTIME=10000 hg -y loop 4
180 \r (no-eol) (esc)
179 \r (no-eol) (esc)
181 loop [ ] 0/4\r (no-eol) (esc)
180 loop [ ] 0/4\r (no-eol) (esc)
182 loop [=========> ] 1/4 8h21m\r (no-eol) (esc)
181 loop [=========> ] 1/4 8h21m\r (no-eol) (esc)
183 loop [====================> ] 2/4 5h34m\r (no-eol) (esc)
182 loop [====================> ] 2/4 5h34m\r (no-eol) (esc)
184 loop [==============================> ] 3/4 2h47m\r (no-eol) (esc)
183 loop [==============================> ] 3/4 2h47m\r (no-eol) (esc)
185 \r (no-eol) (esc)
184 \r (no-eol) (esc)
186
185
187 $ MOCKTIME=1000000 hg -y loop 4
186 $ MOCKTIME=1000000 hg -y loop 4
188 \r (no-eol) (esc)
187 \r (no-eol) (esc)
189 loop [ ] 0/4\r (no-eol) (esc)
188 loop [ ] 0/4\r (no-eol) (esc)
190 loop [=========> ] 1/4 5w00d\r (no-eol) (esc)
189 loop [=========> ] 1/4 5w00d\r (no-eol) (esc)
191 loop [====================> ] 2/4 3w03d\r (no-eol) (esc)
190 loop [====================> ] 2/4 3w03d\r (no-eol) (esc)
192 loop [=============================> ] 3/4 11d14h\r (no-eol) (esc)
191 loop [=============================> ] 3/4 11d14h\r (no-eol) (esc)
193 \r (no-eol) (esc)
192 \r (no-eol) (esc)
194
193
195
194
196 $ MOCKTIME=14000000 hg -y loop 4
195 $ MOCKTIME=14000000 hg -y loop 4
197 \r (no-eol) (esc)
196 \r (no-eol) (esc)
198 loop [ ] 0/4\r (no-eol) (esc)
197 loop [ ] 0/4\r (no-eol) (esc)
199 loop [=========> ] 1/4 1y18w\r (no-eol) (esc)
198 loop [=========> ] 1/4 1y18w\r (no-eol) (esc)
200 loop [===================> ] 2/4 46w03d\r (no-eol) (esc)
199 loop [===================> ] 2/4 46w03d\r (no-eol) (esc)
201 loop [=============================> ] 3/4 23w02d\r (no-eol) (esc)
200 loop [=============================> ] 3/4 23w02d\r (no-eol) (esc)
202 \r (no-eol) (esc)
201 \r (no-eol) (esc)
203
202
204 Time estimates should not fail when there's no end point:
203 Time estimates should not fail when there's no end point:
205 $ hg -y loop -- -4
204 $ hg -y loop -- -4
206 \r (no-eol) (esc)
205 \r (no-eol) (esc)
207 loop [ <=> ] 1\r (no-eol) (esc)
206 loop [ <=> ] 2\r (no-eol) (esc)
208 loop [ <=> ] 2\r (no-eol) (esc)
207 loop [ <=> ] 3\r (no-eol) (esc)
209 loop [ <=> ] 3\r (no-eol) (esc)
210 \r (no-eol) (esc)
208 \r (no-eol) (esc)
General Comments 0
You need to be logged in to leave comments. Login now