##// END OF EJS Templates
blackbox: also log alias expansions...
Augie Fackler -
r29846:318e2b60 default
parent child Browse files
Show More
@@ -1,973 +1,975 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 __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import atexit
10 import atexit
11 import difflib
11 import difflib
12 import errno
12 import errno
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import shlex
16 import shlex
17 import signal
17 import signal
18 import socket
18 import socket
19 import sys
19 import sys
20 import time
20 import time
21 import traceback
21 import traceback
22
22
23
23
24 from .i18n import _
24 from .i18n import _
25
25
26 from . import (
26 from . import (
27 cmdutil,
27 cmdutil,
28 commands,
28 commands,
29 demandimport,
29 demandimport,
30 encoding,
30 encoding,
31 error,
31 error,
32 extensions,
32 extensions,
33 fancyopts,
33 fancyopts,
34 fileset,
34 fileset,
35 hg,
35 hg,
36 hook,
36 hook,
37 profiling,
37 profiling,
38 revset,
38 revset,
39 templatefilters,
39 templatefilters,
40 templatekw,
40 templatekw,
41 templater,
41 templater,
42 ui as uimod,
42 ui as uimod,
43 util,
43 util,
44 )
44 )
45
45
46 class request(object):
46 class request(object):
47 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
47 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
48 ferr=None):
48 ferr=None):
49 self.args = args
49 self.args = args
50 self.ui = ui
50 self.ui = ui
51 self.repo = repo
51 self.repo = repo
52
52
53 # input/output/error streams
53 # input/output/error streams
54 self.fin = fin
54 self.fin = fin
55 self.fout = fout
55 self.fout = fout
56 self.ferr = ferr
56 self.ferr = ferr
57
57
58 def run():
58 def run():
59 "run the command in sys.argv"
59 "run the command in sys.argv"
60 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
60 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
61
61
62 def _getsimilar(symbols, value):
62 def _getsimilar(symbols, value):
63 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
63 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
64 # The cutoff for similarity here is pretty arbitrary. It should
64 # The cutoff for similarity here is pretty arbitrary. It should
65 # probably be investigated and tweaked.
65 # probably be investigated and tweaked.
66 return [s for s in symbols if sim(s) > 0.6]
66 return [s for s in symbols if sim(s) > 0.6]
67
67
68 def _reportsimilar(write, similar):
68 def _reportsimilar(write, similar):
69 if len(similar) == 1:
69 if len(similar) == 1:
70 write(_("(did you mean %s?)\n") % similar[0])
70 write(_("(did you mean %s?)\n") % similar[0])
71 elif similar:
71 elif similar:
72 ss = ", ".join(sorted(similar))
72 ss = ", ".join(sorted(similar))
73 write(_("(did you mean one of %s?)\n") % ss)
73 write(_("(did you mean one of %s?)\n") % ss)
74
74
75 def _formatparse(write, inst):
75 def _formatparse(write, inst):
76 similar = []
76 similar = []
77 if isinstance(inst, error.UnknownIdentifier):
77 if isinstance(inst, error.UnknownIdentifier):
78 # make sure to check fileset first, as revset can invoke fileset
78 # make sure to check fileset first, as revset can invoke fileset
79 similar = _getsimilar(inst.symbols, inst.function)
79 similar = _getsimilar(inst.symbols, inst.function)
80 if len(inst.args) > 1:
80 if len(inst.args) > 1:
81 write(_("hg: parse error at %s: %s\n") %
81 write(_("hg: parse error at %s: %s\n") %
82 (inst.args[1], inst.args[0]))
82 (inst.args[1], inst.args[0]))
83 if (inst.args[0][0] == ' '):
83 if (inst.args[0][0] == ' '):
84 write(_("unexpected leading whitespace\n"))
84 write(_("unexpected leading whitespace\n"))
85 else:
85 else:
86 write(_("hg: parse error: %s\n") % inst.args[0])
86 write(_("hg: parse error: %s\n") % inst.args[0])
87 _reportsimilar(write, similar)
87 _reportsimilar(write, similar)
88 if inst.hint:
88 if inst.hint:
89 write(_("(%s)\n") % inst.hint)
89 write(_("(%s)\n") % inst.hint)
90
90
91 def dispatch(req):
91 def dispatch(req):
92 "run the command specified in req.args"
92 "run the command specified in req.args"
93 if req.ferr:
93 if req.ferr:
94 ferr = req.ferr
94 ferr = req.ferr
95 elif req.ui:
95 elif req.ui:
96 ferr = req.ui.ferr
96 ferr = req.ui.ferr
97 else:
97 else:
98 ferr = sys.stderr
98 ferr = sys.stderr
99
99
100 try:
100 try:
101 if not req.ui:
101 if not req.ui:
102 req.ui = uimod.ui()
102 req.ui = uimod.ui()
103 if '--traceback' in req.args:
103 if '--traceback' in req.args:
104 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
104 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
105
105
106 # set ui streams from the request
106 # set ui streams from the request
107 if req.fin:
107 if req.fin:
108 req.ui.fin = req.fin
108 req.ui.fin = req.fin
109 if req.fout:
109 if req.fout:
110 req.ui.fout = req.fout
110 req.ui.fout = req.fout
111 if req.ferr:
111 if req.ferr:
112 req.ui.ferr = req.ferr
112 req.ui.ferr = req.ferr
113 except error.Abort as inst:
113 except error.Abort as inst:
114 ferr.write(_("abort: %s\n") % inst)
114 ferr.write(_("abort: %s\n") % inst)
115 if inst.hint:
115 if inst.hint:
116 ferr.write(_("(%s)\n") % inst.hint)
116 ferr.write(_("(%s)\n") % inst.hint)
117 return -1
117 return -1
118 except error.ParseError as inst:
118 except error.ParseError as inst:
119 _formatparse(ferr.write, inst)
119 _formatparse(ferr.write, inst)
120 return -1
120 return -1
121
121
122 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
122 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
123 starttime = time.time()
123 starttime = time.time()
124 ret = None
124 ret = None
125 try:
125 try:
126 ret = _runcatch(req)
126 ret = _runcatch(req)
127 except KeyboardInterrupt:
127 except KeyboardInterrupt:
128 try:
128 try:
129 req.ui.warn(_("interrupted!\n"))
129 req.ui.warn(_("interrupted!\n"))
130 except IOError as inst:
130 except IOError as inst:
131 if inst.errno != errno.EPIPE:
131 if inst.errno != errno.EPIPE:
132 raise
132 raise
133 ret = -1
133 ret = -1
134 finally:
134 finally:
135 duration = time.time() - starttime
135 duration = time.time() - starttime
136 req.ui.flush()
136 req.ui.flush()
137 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
137 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
138 msg, ret or 0, duration)
138 msg, ret or 0, duration)
139 return ret
139 return ret
140
140
141 def _runcatch(req):
141 def _runcatch(req):
142 def catchterm(*args):
142 def catchterm(*args):
143 raise error.SignalInterrupt
143 raise error.SignalInterrupt
144
144
145 ui = req.ui
145 ui = req.ui
146 try:
146 try:
147 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
147 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
148 num = getattr(signal, name, None)
148 num = getattr(signal, name, None)
149 if num:
149 if num:
150 signal.signal(num, catchterm)
150 signal.signal(num, catchterm)
151 except ValueError:
151 except ValueError:
152 pass # happens if called in a thread
152 pass # happens if called in a thread
153
153
154 def _runcatchfunc():
154 def _runcatchfunc():
155 try:
155 try:
156 debugger = 'pdb'
156 debugger = 'pdb'
157 debugtrace = {
157 debugtrace = {
158 'pdb' : pdb.set_trace
158 'pdb' : pdb.set_trace
159 }
159 }
160 debugmortem = {
160 debugmortem = {
161 'pdb' : pdb.post_mortem
161 'pdb' : pdb.post_mortem
162 }
162 }
163
163
164 # read --config before doing anything else
164 # read --config before doing anything else
165 # (e.g. to change trust settings for reading .hg/hgrc)
165 # (e.g. to change trust settings for reading .hg/hgrc)
166 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
166 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
167
167
168 if req.repo:
168 if req.repo:
169 # copy configs that were passed on the cmdline (--config) to
169 # copy configs that were passed on the cmdline (--config) to
170 # the repo ui
170 # the repo ui
171 for sec, name, val in cfgs:
171 for sec, name, val in cfgs:
172 req.repo.ui.setconfig(sec, name, val, source='--config')
172 req.repo.ui.setconfig(sec, name, val, source='--config')
173
173
174 # developer config: ui.debugger
174 # developer config: ui.debugger
175 debugger = ui.config("ui", "debugger")
175 debugger = ui.config("ui", "debugger")
176 debugmod = pdb
176 debugmod = pdb
177 if not debugger or ui.plain():
177 if not debugger or ui.plain():
178 # if we are in HGPLAIN mode, then disable custom debugging
178 # if we are in HGPLAIN mode, then disable custom debugging
179 debugger = 'pdb'
179 debugger = 'pdb'
180 elif '--debugger' in req.args:
180 elif '--debugger' in req.args:
181 # This import can be slow for fancy debuggers, so only
181 # This import can be slow for fancy debuggers, so only
182 # do it when absolutely necessary, i.e. when actual
182 # do it when absolutely necessary, i.e. when actual
183 # debugging has been requested
183 # debugging has been requested
184 with demandimport.deactivated():
184 with demandimport.deactivated():
185 try:
185 try:
186 debugmod = __import__(debugger)
186 debugmod = __import__(debugger)
187 except ImportError:
187 except ImportError:
188 pass # Leave debugmod = pdb
188 pass # Leave debugmod = pdb
189
189
190 debugtrace[debugger] = debugmod.set_trace
190 debugtrace[debugger] = debugmod.set_trace
191 debugmortem[debugger] = debugmod.post_mortem
191 debugmortem[debugger] = debugmod.post_mortem
192
192
193 # enter the debugger before command execution
193 # enter the debugger before command execution
194 if '--debugger' in req.args:
194 if '--debugger' in req.args:
195 ui.warn(_("entering debugger - "
195 ui.warn(_("entering debugger - "
196 "type c to continue starting hg or h for help\n"))
196 "type c to continue starting hg or h for help\n"))
197
197
198 if (debugger != 'pdb' and
198 if (debugger != 'pdb' and
199 debugtrace[debugger] == debugtrace['pdb']):
199 debugtrace[debugger] == debugtrace['pdb']):
200 ui.warn(_("%s debugger specified "
200 ui.warn(_("%s debugger specified "
201 "but its module was not found\n") % debugger)
201 "but its module was not found\n") % debugger)
202 with demandimport.deactivated():
202 with demandimport.deactivated():
203 debugtrace[debugger]()
203 debugtrace[debugger]()
204 try:
204 try:
205 return _dispatch(req)
205 return _dispatch(req)
206 finally:
206 finally:
207 ui.flush()
207 ui.flush()
208 except: # re-raises
208 except: # re-raises
209 # enter the debugger when we hit an exception
209 # enter the debugger when we hit an exception
210 if '--debugger' in req.args:
210 if '--debugger' in req.args:
211 traceback.print_exc()
211 traceback.print_exc()
212 debugmortem[debugger](sys.exc_info()[2])
212 debugmortem[debugger](sys.exc_info()[2])
213 ui.traceback()
213 ui.traceback()
214 raise
214 raise
215
215
216 return callcatch(ui, _runcatchfunc)
216 return callcatch(ui, _runcatchfunc)
217
217
218 def callcatch(ui, func):
218 def callcatch(ui, func):
219 """call func() with global exception handling
219 """call func() with global exception handling
220
220
221 return func() if no exception happens. otherwise do some error handling
221 return func() if no exception happens. otherwise do some error handling
222 and return an exit code accordingly.
222 and return an exit code accordingly.
223 """
223 """
224 try:
224 try:
225 return func()
225 return func()
226 # Global exception handling, alphabetically
226 # Global exception handling, alphabetically
227 # Mercurial-specific first, followed by built-in and library exceptions
227 # Mercurial-specific first, followed by built-in and library exceptions
228 except error.AmbiguousCommand as inst:
228 except error.AmbiguousCommand as inst:
229 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
229 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
230 (inst.args[0], " ".join(inst.args[1])))
230 (inst.args[0], " ".join(inst.args[1])))
231 except error.ParseError as inst:
231 except error.ParseError as inst:
232 _formatparse(ui.warn, inst)
232 _formatparse(ui.warn, inst)
233 return -1
233 return -1
234 except error.LockHeld as inst:
234 except error.LockHeld as inst:
235 if inst.errno == errno.ETIMEDOUT:
235 if inst.errno == errno.ETIMEDOUT:
236 reason = _('timed out waiting for lock held by %s') % inst.locker
236 reason = _('timed out waiting for lock held by %s') % inst.locker
237 else:
237 else:
238 reason = _('lock held by %s') % inst.locker
238 reason = _('lock held by %s') % inst.locker
239 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
239 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
240 except error.LockUnavailable as inst:
240 except error.LockUnavailable as inst:
241 ui.warn(_("abort: could not lock %s: %s\n") %
241 ui.warn(_("abort: could not lock %s: %s\n") %
242 (inst.desc or inst.filename, inst.strerror))
242 (inst.desc or inst.filename, inst.strerror))
243 except error.CommandError as inst:
243 except error.CommandError as inst:
244 if inst.args[0]:
244 if inst.args[0]:
245 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
245 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
246 commands.help_(ui, inst.args[0], full=False, command=True)
246 commands.help_(ui, inst.args[0], full=False, command=True)
247 else:
247 else:
248 ui.warn(_("hg: %s\n") % inst.args[1])
248 ui.warn(_("hg: %s\n") % inst.args[1])
249 commands.help_(ui, 'shortlist')
249 commands.help_(ui, 'shortlist')
250 except error.OutOfBandError as inst:
250 except error.OutOfBandError as inst:
251 if inst.args:
251 if inst.args:
252 msg = _("abort: remote error:\n")
252 msg = _("abort: remote error:\n")
253 else:
253 else:
254 msg = _("abort: remote error\n")
254 msg = _("abort: remote error\n")
255 ui.warn(msg)
255 ui.warn(msg)
256 if inst.args:
256 if inst.args:
257 ui.warn(''.join(inst.args))
257 ui.warn(''.join(inst.args))
258 if inst.hint:
258 if inst.hint:
259 ui.warn('(%s)\n' % inst.hint)
259 ui.warn('(%s)\n' % inst.hint)
260 except error.RepoError as inst:
260 except error.RepoError as inst:
261 ui.warn(_("abort: %s!\n") % inst)
261 ui.warn(_("abort: %s!\n") % inst)
262 if inst.hint:
262 if inst.hint:
263 ui.warn(_("(%s)\n") % inst.hint)
263 ui.warn(_("(%s)\n") % inst.hint)
264 except error.ResponseError as inst:
264 except error.ResponseError as inst:
265 ui.warn(_("abort: %s") % inst.args[0])
265 ui.warn(_("abort: %s") % inst.args[0])
266 if not isinstance(inst.args[1], basestring):
266 if not isinstance(inst.args[1], basestring):
267 ui.warn(" %r\n" % (inst.args[1],))
267 ui.warn(" %r\n" % (inst.args[1],))
268 elif not inst.args[1]:
268 elif not inst.args[1]:
269 ui.warn(_(" empty string\n"))
269 ui.warn(_(" empty string\n"))
270 else:
270 else:
271 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
271 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
272 except error.CensoredNodeError as inst:
272 except error.CensoredNodeError as inst:
273 ui.warn(_("abort: file censored %s!\n") % inst)
273 ui.warn(_("abort: file censored %s!\n") % inst)
274 except error.RevlogError as inst:
274 except error.RevlogError as inst:
275 ui.warn(_("abort: %s!\n") % inst)
275 ui.warn(_("abort: %s!\n") % inst)
276 except error.SignalInterrupt:
276 except error.SignalInterrupt:
277 ui.warn(_("killed!\n"))
277 ui.warn(_("killed!\n"))
278 except error.UnknownCommand as inst:
278 except error.UnknownCommand as inst:
279 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
279 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
280 try:
280 try:
281 # check if the command is in a disabled extension
281 # check if the command is in a disabled extension
282 # (but don't check for extensions themselves)
282 # (but don't check for extensions themselves)
283 commands.help_(ui, inst.args[0], unknowncmd=True)
283 commands.help_(ui, inst.args[0], unknowncmd=True)
284 except (error.UnknownCommand, error.Abort):
284 except (error.UnknownCommand, error.Abort):
285 suggested = False
285 suggested = False
286 if len(inst.args) == 2:
286 if len(inst.args) == 2:
287 sim = _getsimilar(inst.args[1], inst.args[0])
287 sim = _getsimilar(inst.args[1], inst.args[0])
288 if sim:
288 if sim:
289 _reportsimilar(ui.warn, sim)
289 _reportsimilar(ui.warn, sim)
290 suggested = True
290 suggested = True
291 if not suggested:
291 if not suggested:
292 commands.help_(ui, 'shortlist')
292 commands.help_(ui, 'shortlist')
293 except error.InterventionRequired as inst:
293 except error.InterventionRequired as inst:
294 ui.warn("%s\n" % inst)
294 ui.warn("%s\n" % inst)
295 if inst.hint:
295 if inst.hint:
296 ui.warn(_("(%s)\n") % inst.hint)
296 ui.warn(_("(%s)\n") % inst.hint)
297 return 1
297 return 1
298 except error.Abort as inst:
298 except error.Abort as inst:
299 ui.warn(_("abort: %s\n") % inst)
299 ui.warn(_("abort: %s\n") % inst)
300 if inst.hint:
300 if inst.hint:
301 ui.warn(_("(%s)\n") % inst.hint)
301 ui.warn(_("(%s)\n") % inst.hint)
302 except ImportError as inst:
302 except ImportError as inst:
303 ui.warn(_("abort: %s!\n") % inst)
303 ui.warn(_("abort: %s!\n") % inst)
304 m = str(inst).split()[-1]
304 m = str(inst).split()[-1]
305 if m in "mpatch bdiff".split():
305 if m in "mpatch bdiff".split():
306 ui.warn(_("(did you forget to compile extensions?)\n"))
306 ui.warn(_("(did you forget to compile extensions?)\n"))
307 elif m in "zlib".split():
307 elif m in "zlib".split():
308 ui.warn(_("(is your Python install correct?)\n"))
308 ui.warn(_("(is your Python install correct?)\n"))
309 except IOError as inst:
309 except IOError as inst:
310 if util.safehasattr(inst, "code"):
310 if util.safehasattr(inst, "code"):
311 ui.warn(_("abort: %s\n") % inst)
311 ui.warn(_("abort: %s\n") % inst)
312 elif util.safehasattr(inst, "reason"):
312 elif util.safehasattr(inst, "reason"):
313 try: # usually it is in the form (errno, strerror)
313 try: # usually it is in the form (errno, strerror)
314 reason = inst.reason.args[1]
314 reason = inst.reason.args[1]
315 except (AttributeError, IndexError):
315 except (AttributeError, IndexError):
316 # it might be anything, for example a string
316 # it might be anything, for example a string
317 reason = inst.reason
317 reason = inst.reason
318 if isinstance(reason, unicode):
318 if isinstance(reason, unicode):
319 # SSLError of Python 2.7.9 contains a unicode
319 # SSLError of Python 2.7.9 contains a unicode
320 reason = reason.encode(encoding.encoding, 'replace')
320 reason = reason.encode(encoding.encoding, 'replace')
321 ui.warn(_("abort: error: %s\n") % reason)
321 ui.warn(_("abort: error: %s\n") % reason)
322 elif (util.safehasattr(inst, "args")
322 elif (util.safehasattr(inst, "args")
323 and inst.args and inst.args[0] == errno.EPIPE):
323 and inst.args and inst.args[0] == errno.EPIPE):
324 pass
324 pass
325 elif getattr(inst, "strerror", None):
325 elif getattr(inst, "strerror", None):
326 if getattr(inst, "filename", None):
326 if getattr(inst, "filename", None):
327 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
327 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
328 else:
328 else:
329 ui.warn(_("abort: %s\n") % inst.strerror)
329 ui.warn(_("abort: %s\n") % inst.strerror)
330 else:
330 else:
331 raise
331 raise
332 except OSError as inst:
332 except OSError as inst:
333 if getattr(inst, "filename", None) is not None:
333 if getattr(inst, "filename", None) is not None:
334 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
334 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
335 else:
335 else:
336 ui.warn(_("abort: %s\n") % inst.strerror)
336 ui.warn(_("abort: %s\n") % inst.strerror)
337 except KeyboardInterrupt:
337 except KeyboardInterrupt:
338 raise
338 raise
339 except MemoryError:
339 except MemoryError:
340 ui.warn(_("abort: out of memory\n"))
340 ui.warn(_("abort: out of memory\n"))
341 except SystemExit as inst:
341 except SystemExit as inst:
342 # Commands shouldn't sys.exit directly, but give a return code.
342 # Commands shouldn't sys.exit directly, but give a return code.
343 # Just in case catch this and and pass exit code to caller.
343 # Just in case catch this and and pass exit code to caller.
344 return inst.code
344 return inst.code
345 except socket.error as inst:
345 except socket.error as inst:
346 ui.warn(_("abort: %s\n") % inst.args[-1])
346 ui.warn(_("abort: %s\n") % inst.args[-1])
347 except: # perhaps re-raises
347 except: # perhaps re-raises
348 if not handlecommandexception(ui):
348 if not handlecommandexception(ui):
349 raise
349 raise
350
350
351 return -1
351 return -1
352
352
353 def aliasargs(fn, givenargs):
353 def aliasargs(fn, givenargs):
354 args = getattr(fn, 'args', [])
354 args = getattr(fn, 'args', [])
355 if args:
355 if args:
356 cmd = ' '.join(map(util.shellquote, args))
356 cmd = ' '.join(map(util.shellquote, args))
357
357
358 nums = []
358 nums = []
359 def replacer(m):
359 def replacer(m):
360 num = int(m.group(1)) - 1
360 num = int(m.group(1)) - 1
361 nums.append(num)
361 nums.append(num)
362 if num < len(givenargs):
362 if num < len(givenargs):
363 return givenargs[num]
363 return givenargs[num]
364 raise error.Abort(_('too few arguments for command alias'))
364 raise error.Abort(_('too few arguments for command alias'))
365 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
365 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
366 givenargs = [x for i, x in enumerate(givenargs)
366 givenargs = [x for i, x in enumerate(givenargs)
367 if i not in nums]
367 if i not in nums]
368 args = shlex.split(cmd)
368 args = shlex.split(cmd)
369 return args + givenargs
369 return args + givenargs
370
370
371 def aliasinterpolate(name, args, cmd):
371 def aliasinterpolate(name, args, cmd):
372 '''interpolate args into cmd for shell aliases
372 '''interpolate args into cmd for shell aliases
373
373
374 This also handles $0, $@ and "$@".
374 This also handles $0, $@ and "$@".
375 '''
375 '''
376 # util.interpolate can't deal with "$@" (with quotes) because it's only
376 # util.interpolate can't deal with "$@" (with quotes) because it's only
377 # built to match prefix + patterns.
377 # built to match prefix + patterns.
378 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
378 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
379 replacemap['$0'] = name
379 replacemap['$0'] = name
380 replacemap['$$'] = '$'
380 replacemap['$$'] = '$'
381 replacemap['$@'] = ' '.join(args)
381 replacemap['$@'] = ' '.join(args)
382 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
382 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
383 # parameters, separated out into words. Emulate the same behavior here by
383 # parameters, separated out into words. Emulate the same behavior here by
384 # quoting the arguments individually. POSIX shells will then typically
384 # quoting the arguments individually. POSIX shells will then typically
385 # tokenize each argument into exactly one word.
385 # tokenize each argument into exactly one word.
386 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
386 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
387 # escape '\$' for regex
387 # escape '\$' for regex
388 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
388 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
389 r = re.compile(regex)
389 r = re.compile(regex)
390 return r.sub(lambda x: replacemap[x.group()], cmd)
390 return r.sub(lambda x: replacemap[x.group()], cmd)
391
391
392 class cmdalias(object):
392 class cmdalias(object):
393 def __init__(self, name, definition, cmdtable, source):
393 def __init__(self, name, definition, cmdtable, source):
394 self.name = self.cmd = name
394 self.name = self.cmd = name
395 self.cmdname = ''
395 self.cmdname = ''
396 self.definition = definition
396 self.definition = definition
397 self.fn = None
397 self.fn = None
398 self.givenargs = []
398 self.givenargs = []
399 self.opts = []
399 self.opts = []
400 self.help = ''
400 self.help = ''
401 self.badalias = None
401 self.badalias = None
402 self.unknowncmd = False
402 self.unknowncmd = False
403 self.source = source
403 self.source = source
404
404
405 try:
405 try:
406 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
406 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
407 for alias, e in cmdtable.iteritems():
407 for alias, e in cmdtable.iteritems():
408 if e is entry:
408 if e is entry:
409 self.cmd = alias
409 self.cmd = alias
410 break
410 break
411 self.shadows = True
411 self.shadows = True
412 except error.UnknownCommand:
412 except error.UnknownCommand:
413 self.shadows = False
413 self.shadows = False
414
414
415 if not self.definition:
415 if not self.definition:
416 self.badalias = _("no definition for alias '%s'") % self.name
416 self.badalias = _("no definition for alias '%s'") % self.name
417 return
417 return
418
418
419 if self.definition.startswith('!'):
419 if self.definition.startswith('!'):
420 self.shell = True
420 self.shell = True
421 def fn(ui, *args):
421 def fn(ui, *args):
422 env = {'HG_ARGS': ' '.join((self.name,) + args)}
422 env = {'HG_ARGS': ' '.join((self.name,) + args)}
423 def _checkvar(m):
423 def _checkvar(m):
424 if m.groups()[0] == '$':
424 if m.groups()[0] == '$':
425 return m.group()
425 return m.group()
426 elif int(m.groups()[0]) <= len(args):
426 elif int(m.groups()[0]) <= len(args):
427 return m.group()
427 return m.group()
428 else:
428 else:
429 ui.debug("No argument found for substitution "
429 ui.debug("No argument found for substitution "
430 "of %i variable in alias '%s' definition."
430 "of %i variable in alias '%s' definition."
431 % (int(m.groups()[0]), self.name))
431 % (int(m.groups()[0]), self.name))
432 return ''
432 return ''
433 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
433 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
434 cmd = aliasinterpolate(self.name, args, cmd)
434 cmd = aliasinterpolate(self.name, args, cmd)
435 return ui.system(cmd, environ=env)
435 return ui.system(cmd, environ=env)
436 self.fn = fn
436 self.fn = fn
437 return
437 return
438
438
439 try:
439 try:
440 args = shlex.split(self.definition)
440 args = shlex.split(self.definition)
441 except ValueError as inst:
441 except ValueError as inst:
442 self.badalias = (_("error in definition for alias '%s': %s")
442 self.badalias = (_("error in definition for alias '%s': %s")
443 % (self.name, inst))
443 % (self.name, inst))
444 return
444 return
445 self.cmdname = cmd = args.pop(0)
445 self.cmdname = cmd = args.pop(0)
446 self.givenargs = args
446 self.givenargs = args
447
447
448 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
448 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
449 if _earlygetopt([invalidarg], args):
449 if _earlygetopt([invalidarg], args):
450 self.badalias = (_("error in definition for alias '%s': %s may "
450 self.badalias = (_("error in definition for alias '%s': %s may "
451 "only be given on the command line")
451 "only be given on the command line")
452 % (self.name, invalidarg))
452 % (self.name, invalidarg))
453 return
453 return
454
454
455 try:
455 try:
456 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
456 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
457 if len(tableentry) > 2:
457 if len(tableentry) > 2:
458 self.fn, self.opts, self.help = tableentry
458 self.fn, self.opts, self.help = tableentry
459 else:
459 else:
460 self.fn, self.opts = tableentry
460 self.fn, self.opts = tableentry
461
461
462 if self.help.startswith("hg " + cmd):
462 if self.help.startswith("hg " + cmd):
463 # drop prefix in old-style help lines so hg shows the alias
463 # drop prefix in old-style help lines so hg shows the alias
464 self.help = self.help[4 + len(cmd):]
464 self.help = self.help[4 + len(cmd):]
465 self.__doc__ = self.fn.__doc__
465 self.__doc__ = self.fn.__doc__
466
466
467 except error.UnknownCommand:
467 except error.UnknownCommand:
468 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
468 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
469 % (self.name, cmd))
469 % (self.name, cmd))
470 self.unknowncmd = True
470 self.unknowncmd = True
471 except error.AmbiguousCommand:
471 except error.AmbiguousCommand:
472 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
472 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
473 % (self.name, cmd))
473 % (self.name, cmd))
474
474
475 @property
475 @property
476 def args(self):
476 def args(self):
477 args = map(util.expandpath, self.givenargs)
477 args = map(util.expandpath, self.givenargs)
478 return aliasargs(self.fn, args)
478 return aliasargs(self.fn, args)
479
479
480 def __getattr__(self, name):
480 def __getattr__(self, name):
481 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
481 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
482 if name not in adefaults:
482 if name not in adefaults:
483 raise AttributeError(name)
483 raise AttributeError(name)
484 if self.badalias or util.safehasattr(self, 'shell'):
484 if self.badalias or util.safehasattr(self, 'shell'):
485 return adefaults[name]
485 return adefaults[name]
486 return getattr(self.fn, name)
486 return getattr(self.fn, name)
487
487
488 def __call__(self, ui, *args, **opts):
488 def __call__(self, ui, *args, **opts):
489 if self.badalias:
489 if self.badalias:
490 hint = None
490 hint = None
491 if self.unknowncmd:
491 if self.unknowncmd:
492 try:
492 try:
493 # check if the command is in a disabled extension
493 # check if the command is in a disabled extension
494 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
494 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
495 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
495 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
496 except error.UnknownCommand:
496 except error.UnknownCommand:
497 pass
497 pass
498 raise error.Abort(self.badalias, hint=hint)
498 raise error.Abort(self.badalias, hint=hint)
499 if self.shadows:
499 if self.shadows:
500 ui.debug("alias '%s' shadows command '%s'\n" %
500 ui.debug("alias '%s' shadows command '%s'\n" %
501 (self.name, self.cmdname))
501 (self.name, self.cmdname))
502
502
503 ui.log('commandalias', "alias '%s' expands to '%s'\n",
504 self.name, self.definition)
503 if util.safehasattr(self, 'shell'):
505 if util.safehasattr(self, 'shell'):
504 return self.fn(ui, *args, **opts)
506 return self.fn(ui, *args, **opts)
505 else:
507 else:
506 try:
508 try:
507 return util.checksignature(self.fn)(ui, *args, **opts)
509 return util.checksignature(self.fn)(ui, *args, **opts)
508 except error.SignatureError:
510 except error.SignatureError:
509 args = ' '.join([self.cmdname] + self.args)
511 args = ' '.join([self.cmdname] + self.args)
510 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
512 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
511 raise
513 raise
512
514
513 def addaliases(ui, cmdtable):
515 def addaliases(ui, cmdtable):
514 # aliases are processed after extensions have been loaded, so they
516 # aliases are processed after extensions have been loaded, so they
515 # may use extension commands. Aliases can also use other alias definitions,
517 # may use extension commands. Aliases can also use other alias definitions,
516 # but only if they have been defined prior to the current definition.
518 # but only if they have been defined prior to the current definition.
517 for alias, definition in ui.configitems('alias'):
519 for alias, definition in ui.configitems('alias'):
518 source = ui.configsource('alias', alias)
520 source = ui.configsource('alias', alias)
519 aliasdef = cmdalias(alias, definition, cmdtable, source)
521 aliasdef = cmdalias(alias, definition, cmdtable, source)
520
522
521 try:
523 try:
522 olddef = cmdtable[aliasdef.cmd][0]
524 olddef = cmdtable[aliasdef.cmd][0]
523 if olddef.definition == aliasdef.definition:
525 if olddef.definition == aliasdef.definition:
524 continue
526 continue
525 except (KeyError, AttributeError):
527 except (KeyError, AttributeError):
526 # definition might not exist or it might not be a cmdalias
528 # definition might not exist or it might not be a cmdalias
527 pass
529 pass
528
530
529 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
531 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
530
532
531 def _parse(ui, args):
533 def _parse(ui, args):
532 options = {}
534 options = {}
533 cmdoptions = {}
535 cmdoptions = {}
534
536
535 try:
537 try:
536 args = fancyopts.fancyopts(args, commands.globalopts, options)
538 args = fancyopts.fancyopts(args, commands.globalopts, options)
537 except fancyopts.getopt.GetoptError as inst:
539 except fancyopts.getopt.GetoptError as inst:
538 raise error.CommandError(None, inst)
540 raise error.CommandError(None, inst)
539
541
540 if args:
542 if args:
541 cmd, args = args[0], args[1:]
543 cmd, args = args[0], args[1:]
542 aliases, entry = cmdutil.findcmd(cmd, commands.table,
544 aliases, entry = cmdutil.findcmd(cmd, commands.table,
543 ui.configbool("ui", "strict"))
545 ui.configbool("ui", "strict"))
544 cmd = aliases[0]
546 cmd = aliases[0]
545 args = aliasargs(entry[0], args)
547 args = aliasargs(entry[0], args)
546 defaults = ui.config("defaults", cmd)
548 defaults = ui.config("defaults", cmd)
547 if defaults:
549 if defaults:
548 args = map(util.expandpath, shlex.split(defaults)) + args
550 args = map(util.expandpath, shlex.split(defaults)) + args
549 c = list(entry[1])
551 c = list(entry[1])
550 else:
552 else:
551 cmd = None
553 cmd = None
552 c = []
554 c = []
553
555
554 # combine global options into local
556 # combine global options into local
555 for o in commands.globalopts:
557 for o in commands.globalopts:
556 c.append((o[0], o[1], options[o[1]], o[3]))
558 c.append((o[0], o[1], options[o[1]], o[3]))
557
559
558 try:
560 try:
559 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
561 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
560 except fancyopts.getopt.GetoptError as inst:
562 except fancyopts.getopt.GetoptError as inst:
561 raise error.CommandError(cmd, inst)
563 raise error.CommandError(cmd, inst)
562
564
563 # separate global options back out
565 # separate global options back out
564 for o in commands.globalopts:
566 for o in commands.globalopts:
565 n = o[1]
567 n = o[1]
566 options[n] = cmdoptions[n]
568 options[n] = cmdoptions[n]
567 del cmdoptions[n]
569 del cmdoptions[n]
568
570
569 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
571 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
570
572
571 def _parseconfig(ui, config):
573 def _parseconfig(ui, config):
572 """parse the --config options from the command line"""
574 """parse the --config options from the command line"""
573 configs = []
575 configs = []
574
576
575 for cfg in config:
577 for cfg in config:
576 try:
578 try:
577 name, value = [cfgelem.strip()
579 name, value = [cfgelem.strip()
578 for cfgelem in cfg.split('=', 1)]
580 for cfgelem in cfg.split('=', 1)]
579 section, name = name.split('.', 1)
581 section, name = name.split('.', 1)
580 if not section or not name:
582 if not section or not name:
581 raise IndexError
583 raise IndexError
582 ui.setconfig(section, name, value, '--config')
584 ui.setconfig(section, name, value, '--config')
583 configs.append((section, name, value))
585 configs.append((section, name, value))
584 except (IndexError, ValueError):
586 except (IndexError, ValueError):
585 raise error.Abort(_('malformed --config option: %r '
587 raise error.Abort(_('malformed --config option: %r '
586 '(use --config section.name=value)') % cfg)
588 '(use --config section.name=value)') % cfg)
587
589
588 return configs
590 return configs
589
591
590 def _earlygetopt(aliases, args):
592 def _earlygetopt(aliases, args):
591 """Return list of values for an option (or aliases).
593 """Return list of values for an option (or aliases).
592
594
593 The values are listed in the order they appear in args.
595 The values are listed in the order they appear in args.
594 The options and values are removed from args.
596 The options and values are removed from args.
595
597
596 >>> args = ['x', '--cwd', 'foo', 'y']
598 >>> args = ['x', '--cwd', 'foo', 'y']
597 >>> _earlygetopt(['--cwd'], args), args
599 >>> _earlygetopt(['--cwd'], args), args
598 (['foo'], ['x', 'y'])
600 (['foo'], ['x', 'y'])
599
601
600 >>> args = ['x', '--cwd=bar', 'y']
602 >>> args = ['x', '--cwd=bar', 'y']
601 >>> _earlygetopt(['--cwd'], args), args
603 >>> _earlygetopt(['--cwd'], args), args
602 (['bar'], ['x', 'y'])
604 (['bar'], ['x', 'y'])
603
605
604 >>> args = ['x', '-R', 'foo', 'y']
606 >>> args = ['x', '-R', 'foo', 'y']
605 >>> _earlygetopt(['-R'], args), args
607 >>> _earlygetopt(['-R'], args), args
606 (['foo'], ['x', 'y'])
608 (['foo'], ['x', 'y'])
607
609
608 >>> args = ['x', '-Rbar', 'y']
610 >>> args = ['x', '-Rbar', 'y']
609 >>> _earlygetopt(['-R'], args), args
611 >>> _earlygetopt(['-R'], args), args
610 (['bar'], ['x', 'y'])
612 (['bar'], ['x', 'y'])
611 """
613 """
612 try:
614 try:
613 argcount = args.index("--")
615 argcount = args.index("--")
614 except ValueError:
616 except ValueError:
615 argcount = len(args)
617 argcount = len(args)
616 shortopts = [opt for opt in aliases if len(opt) == 2]
618 shortopts = [opt for opt in aliases if len(opt) == 2]
617 values = []
619 values = []
618 pos = 0
620 pos = 0
619 while pos < argcount:
621 while pos < argcount:
620 fullarg = arg = args[pos]
622 fullarg = arg = args[pos]
621 equals = arg.find('=')
623 equals = arg.find('=')
622 if equals > -1:
624 if equals > -1:
623 arg = arg[:equals]
625 arg = arg[:equals]
624 if arg in aliases:
626 if arg in aliases:
625 del args[pos]
627 del args[pos]
626 if equals > -1:
628 if equals > -1:
627 values.append(fullarg[equals + 1:])
629 values.append(fullarg[equals + 1:])
628 argcount -= 1
630 argcount -= 1
629 else:
631 else:
630 if pos + 1 >= argcount:
632 if pos + 1 >= argcount:
631 # ignore and let getopt report an error if there is no value
633 # ignore and let getopt report an error if there is no value
632 break
634 break
633 values.append(args.pop(pos))
635 values.append(args.pop(pos))
634 argcount -= 2
636 argcount -= 2
635 elif arg[:2] in shortopts:
637 elif arg[:2] in shortopts:
636 # short option can have no following space, e.g. hg log -Rfoo
638 # short option can have no following space, e.g. hg log -Rfoo
637 values.append(args.pop(pos)[2:])
639 values.append(args.pop(pos)[2:])
638 argcount -= 1
640 argcount -= 1
639 else:
641 else:
640 pos += 1
642 pos += 1
641 return values
643 return values
642
644
643 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
645 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
644 # run pre-hook, and abort if it fails
646 # run pre-hook, and abort if it fails
645 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
647 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
646 pats=cmdpats, opts=cmdoptions)
648 pats=cmdpats, opts=cmdoptions)
647 try:
649 try:
648 ret = _runcommand(ui, options, cmd, d)
650 ret = _runcommand(ui, options, cmd, d)
649 # run post-hook, passing command result
651 # run post-hook, passing command result
650 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
652 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
651 result=ret, pats=cmdpats, opts=cmdoptions)
653 result=ret, pats=cmdpats, opts=cmdoptions)
652 except Exception:
654 except Exception:
653 # run failure hook and re-raise
655 # run failure hook and re-raise
654 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
656 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
655 pats=cmdpats, opts=cmdoptions)
657 pats=cmdpats, opts=cmdoptions)
656 raise
658 raise
657 return ret
659 return ret
658
660
659 def _getlocal(ui, rpath, wd=None):
661 def _getlocal(ui, rpath, wd=None):
660 """Return (path, local ui object) for the given target path.
662 """Return (path, local ui object) for the given target path.
661
663
662 Takes paths in [cwd]/.hg/hgrc into account."
664 Takes paths in [cwd]/.hg/hgrc into account."
663 """
665 """
664 if wd is None:
666 if wd is None:
665 try:
667 try:
666 wd = os.getcwd()
668 wd = os.getcwd()
667 except OSError as e:
669 except OSError as e:
668 raise error.Abort(_("error getting current working directory: %s") %
670 raise error.Abort(_("error getting current working directory: %s") %
669 e.strerror)
671 e.strerror)
670 path = cmdutil.findrepo(wd) or ""
672 path = cmdutil.findrepo(wd) or ""
671 if not path:
673 if not path:
672 lui = ui
674 lui = ui
673 else:
675 else:
674 lui = ui.copy()
676 lui = ui.copy()
675 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
677 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
676
678
677 if rpath and rpath[-1]:
679 if rpath and rpath[-1]:
678 path = lui.expandpath(rpath[-1])
680 path = lui.expandpath(rpath[-1])
679 lui = ui.copy()
681 lui = ui.copy()
680 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
682 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
681
683
682 return path, lui
684 return path, lui
683
685
684 def _checkshellalias(lui, ui, args):
686 def _checkshellalias(lui, ui, args):
685 """Return the function to run the shell alias, if it is required"""
687 """Return the function to run the shell alias, if it is required"""
686 options = {}
688 options = {}
687
689
688 try:
690 try:
689 args = fancyopts.fancyopts(args, commands.globalopts, options)
691 args = fancyopts.fancyopts(args, commands.globalopts, options)
690 except fancyopts.getopt.GetoptError:
692 except fancyopts.getopt.GetoptError:
691 return
693 return
692
694
693 if not args:
695 if not args:
694 return
696 return
695
697
696 cmdtable = commands.table
698 cmdtable = commands.table
697
699
698 cmd = args[0]
700 cmd = args[0]
699 try:
701 try:
700 strict = ui.configbool("ui", "strict")
702 strict = ui.configbool("ui", "strict")
701 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
703 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
702 except (error.AmbiguousCommand, error.UnknownCommand):
704 except (error.AmbiguousCommand, error.UnknownCommand):
703 return
705 return
704
706
705 cmd = aliases[0]
707 cmd = aliases[0]
706 fn = entry[0]
708 fn = entry[0]
707
709
708 if cmd and util.safehasattr(fn, 'shell'):
710 if cmd and util.safehasattr(fn, 'shell'):
709 d = lambda: fn(ui, *args[1:])
711 d = lambda: fn(ui, *args[1:])
710 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
712 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
711 [], {})
713 [], {})
712
714
713 def _cmdattr(ui, cmd, func, attr):
715 def _cmdattr(ui, cmd, func, attr):
714 try:
716 try:
715 return getattr(func, attr)
717 return getattr(func, attr)
716 except AttributeError:
718 except AttributeError:
717 ui.deprecwarn("missing attribute '%s', use @command decorator "
719 ui.deprecwarn("missing attribute '%s', use @command decorator "
718 "to register '%s'" % (attr, cmd), '3.8')
720 "to register '%s'" % (attr, cmd), '3.8')
719 return False
721 return False
720
722
721 _loaded = set()
723 _loaded = set()
722
724
723 # list of (objname, loadermod, loadername) tuple:
725 # list of (objname, loadermod, loadername) tuple:
724 # - objname is the name of an object in extension module, from which
726 # - objname is the name of an object in extension module, from which
725 # extra information is loaded
727 # extra information is loaded
726 # - loadermod is the module where loader is placed
728 # - loadermod is the module where loader is placed
727 # - loadername is the name of the function, which takes (ui, extensionname,
729 # - loadername is the name of the function, which takes (ui, extensionname,
728 # extraobj) arguments
730 # extraobj) arguments
729 extraloaders = [
731 extraloaders = [
730 ('cmdtable', commands, 'loadcmdtable'),
732 ('cmdtable', commands, 'loadcmdtable'),
731 ('filesetpredicate', fileset, 'loadpredicate'),
733 ('filesetpredicate', fileset, 'loadpredicate'),
732 ('revsetpredicate', revset, 'loadpredicate'),
734 ('revsetpredicate', revset, 'loadpredicate'),
733 ('templatefilter', templatefilters, 'loadfilter'),
735 ('templatefilter', templatefilters, 'loadfilter'),
734 ('templatefunc', templater, 'loadfunction'),
736 ('templatefunc', templater, 'loadfunction'),
735 ('templatekeyword', templatekw, 'loadkeyword'),
737 ('templatekeyword', templatekw, 'loadkeyword'),
736 ]
738 ]
737
739
738 def _dispatch(req):
740 def _dispatch(req):
739 args = req.args
741 args = req.args
740 ui = req.ui
742 ui = req.ui
741
743
742 # check for cwd
744 # check for cwd
743 cwd = _earlygetopt(['--cwd'], args)
745 cwd = _earlygetopt(['--cwd'], args)
744 if cwd:
746 if cwd:
745 os.chdir(cwd[-1])
747 os.chdir(cwd[-1])
746
748
747 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
749 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
748 path, lui = _getlocal(ui, rpath)
750 path, lui = _getlocal(ui, rpath)
749
751
750 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
752 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
751 # reposetup. Programs like TortoiseHg will call _dispatch several
753 # reposetup. Programs like TortoiseHg will call _dispatch several
752 # times so we keep track of configured extensions in _loaded.
754 # times so we keep track of configured extensions in _loaded.
753 extensions.loadall(lui)
755 extensions.loadall(lui)
754 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
756 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
755 # Propagate any changes to lui.__class__ by extensions
757 # Propagate any changes to lui.__class__ by extensions
756 ui.__class__ = lui.__class__
758 ui.__class__ = lui.__class__
757
759
758 # (uisetup and extsetup are handled in extensions.loadall)
760 # (uisetup and extsetup are handled in extensions.loadall)
759
761
760 for name, module in exts:
762 for name, module in exts:
761 for objname, loadermod, loadername in extraloaders:
763 for objname, loadermod, loadername in extraloaders:
762 extraobj = getattr(module, objname, None)
764 extraobj = getattr(module, objname, None)
763 if extraobj is not None:
765 if extraobj is not None:
764 getattr(loadermod, loadername)(ui, name, extraobj)
766 getattr(loadermod, loadername)(ui, name, extraobj)
765 _loaded.add(name)
767 _loaded.add(name)
766
768
767 # (reposetup is handled in hg.repository)
769 # (reposetup is handled in hg.repository)
768
770
769 addaliases(lui, commands.table)
771 addaliases(lui, commands.table)
770
772
771 # All aliases and commands are completely defined, now.
773 # All aliases and commands are completely defined, now.
772 # Check abbreviation/ambiguity of shell alias.
774 # Check abbreviation/ambiguity of shell alias.
773 shellaliasfn = _checkshellalias(lui, ui, args)
775 shellaliasfn = _checkshellalias(lui, ui, args)
774 if shellaliasfn:
776 if shellaliasfn:
775 return shellaliasfn()
777 return shellaliasfn()
776
778
777 # check for fallback encoding
779 # check for fallback encoding
778 fallback = lui.config('ui', 'fallbackencoding')
780 fallback = lui.config('ui', 'fallbackencoding')
779 if fallback:
781 if fallback:
780 encoding.fallbackencoding = fallback
782 encoding.fallbackencoding = fallback
781
783
782 fullargs = args
784 fullargs = args
783 cmd, func, args, options, cmdoptions = _parse(lui, args)
785 cmd, func, args, options, cmdoptions = _parse(lui, args)
784
786
785 if options["config"]:
787 if options["config"]:
786 raise error.Abort(_("option --config may not be abbreviated!"))
788 raise error.Abort(_("option --config may not be abbreviated!"))
787 if options["cwd"]:
789 if options["cwd"]:
788 raise error.Abort(_("option --cwd may not be abbreviated!"))
790 raise error.Abort(_("option --cwd may not be abbreviated!"))
789 if options["repository"]:
791 if options["repository"]:
790 raise error.Abort(_(
792 raise error.Abort(_(
791 "option -R has to be separated from other options (e.g. not -qR) "
793 "option -R has to be separated from other options (e.g. not -qR) "
792 "and --repository may only be abbreviated as --repo!"))
794 "and --repository may only be abbreviated as --repo!"))
793
795
794 if options["encoding"]:
796 if options["encoding"]:
795 encoding.encoding = options["encoding"]
797 encoding.encoding = options["encoding"]
796 if options["encodingmode"]:
798 if options["encodingmode"]:
797 encoding.encodingmode = options["encodingmode"]
799 encoding.encodingmode = options["encodingmode"]
798 if options["time"]:
800 if options["time"]:
799 def get_times():
801 def get_times():
800 t = os.times()
802 t = os.times()
801 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
803 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
802 t = (t[0], t[1], t[2], t[3], time.clock())
804 t = (t[0], t[1], t[2], t[3], time.clock())
803 return t
805 return t
804 s = get_times()
806 s = get_times()
805 def print_time():
807 def print_time():
806 t = get_times()
808 t = get_times()
807 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
809 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
808 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
810 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
809 atexit.register(print_time)
811 atexit.register(print_time)
810
812
811 uis = set([ui, lui])
813 uis = set([ui, lui])
812
814
813 if req.repo:
815 if req.repo:
814 uis.add(req.repo.ui)
816 uis.add(req.repo.ui)
815
817
816 if options['verbose'] or options['debug'] or options['quiet']:
818 if options['verbose'] or options['debug'] or options['quiet']:
817 for opt in ('verbose', 'debug', 'quiet'):
819 for opt in ('verbose', 'debug', 'quiet'):
818 val = str(bool(options[opt]))
820 val = str(bool(options[opt]))
819 for ui_ in uis:
821 for ui_ in uis:
820 ui_.setconfig('ui', opt, val, '--' + opt)
822 ui_.setconfig('ui', opt, val, '--' + opt)
821
823
822 if options['profile']:
824 if options['profile']:
823 for ui_ in uis:
825 for ui_ in uis:
824 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
826 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
825
827
826 if options['traceback']:
828 if options['traceback']:
827 for ui_ in uis:
829 for ui_ in uis:
828 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
830 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
829
831
830 if options['noninteractive']:
832 if options['noninteractive']:
831 for ui_ in uis:
833 for ui_ in uis:
832 ui_.setconfig('ui', 'interactive', 'off', '-y')
834 ui_.setconfig('ui', 'interactive', 'off', '-y')
833
835
834 if cmdoptions.get('insecure', False):
836 if cmdoptions.get('insecure', False):
835 for ui_ in uis:
837 for ui_ in uis:
836 ui_.insecureconnections = True
838 ui_.insecureconnections = True
837
839
838 if options['version']:
840 if options['version']:
839 return commands.version_(ui)
841 return commands.version_(ui)
840 if options['help']:
842 if options['help']:
841 return commands.help_(ui, cmd, command=cmd is not None)
843 return commands.help_(ui, cmd, command=cmd is not None)
842 elif not cmd:
844 elif not cmd:
843 return commands.help_(ui, 'shortlist')
845 return commands.help_(ui, 'shortlist')
844
846
845 repo = None
847 repo = None
846 cmdpats = args[:]
848 cmdpats = args[:]
847 if not _cmdattr(ui, cmd, func, 'norepo'):
849 if not _cmdattr(ui, cmd, func, 'norepo'):
848 # use the repo from the request only if we don't have -R
850 # use the repo from the request only if we don't have -R
849 if not rpath and not cwd:
851 if not rpath and not cwd:
850 repo = req.repo
852 repo = req.repo
851
853
852 if repo:
854 if repo:
853 # set the descriptors of the repo ui to those of ui
855 # set the descriptors of the repo ui to those of ui
854 repo.ui.fin = ui.fin
856 repo.ui.fin = ui.fin
855 repo.ui.fout = ui.fout
857 repo.ui.fout = ui.fout
856 repo.ui.ferr = ui.ferr
858 repo.ui.ferr = ui.ferr
857 else:
859 else:
858 try:
860 try:
859 repo = hg.repository(ui, path=path)
861 repo = hg.repository(ui, path=path)
860 if not repo.local():
862 if not repo.local():
861 raise error.Abort(_("repository '%s' is not local") % path)
863 raise error.Abort(_("repository '%s' is not local") % path)
862 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
864 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
863 except error.RequirementError:
865 except error.RequirementError:
864 raise
866 raise
865 except error.RepoError:
867 except error.RepoError:
866 if rpath and rpath[-1]: # invalid -R path
868 if rpath and rpath[-1]: # invalid -R path
867 raise
869 raise
868 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
870 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
869 if (_cmdattr(ui, cmd, func, 'inferrepo') and
871 if (_cmdattr(ui, cmd, func, 'inferrepo') and
870 args and not path):
872 args and not path):
871 # try to infer -R from command args
873 # try to infer -R from command args
872 repos = map(cmdutil.findrepo, args)
874 repos = map(cmdutil.findrepo, args)
873 guess = repos[0]
875 guess = repos[0]
874 if guess and repos.count(guess) == len(repos):
876 if guess and repos.count(guess) == len(repos):
875 req.args = ['--repository', guess] + fullargs
877 req.args = ['--repository', guess] + fullargs
876 return _dispatch(req)
878 return _dispatch(req)
877 if not path:
879 if not path:
878 raise error.RepoError(_("no repository found in '%s'"
880 raise error.RepoError(_("no repository found in '%s'"
879 " (.hg not found)")
881 " (.hg not found)")
880 % os.getcwd())
882 % os.getcwd())
881 raise
883 raise
882 if repo:
884 if repo:
883 ui = repo.ui
885 ui = repo.ui
884 if options['hidden']:
886 if options['hidden']:
885 repo = repo.unfiltered()
887 repo = repo.unfiltered()
886 args.insert(0, repo)
888 args.insert(0, repo)
887 elif rpath:
889 elif rpath:
888 ui.warn(_("warning: --repository ignored\n"))
890 ui.warn(_("warning: --repository ignored\n"))
889
891
890 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
892 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
891 ui.log("command", '%s\n', msg)
893 ui.log("command", '%s\n', msg)
892 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
894 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
893 try:
895 try:
894 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
896 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
895 cmdpats, cmdoptions)
897 cmdpats, cmdoptions)
896 finally:
898 finally:
897 if repo and repo != req.repo:
899 if repo and repo != req.repo:
898 repo.close()
900 repo.close()
899
901
900 def _runcommand(ui, options, cmd, cmdfunc):
902 def _runcommand(ui, options, cmd, cmdfunc):
901 """Run a command function, possibly with profiling enabled."""
903 """Run a command function, possibly with profiling enabled."""
902 with profiling.maybeprofile(ui):
904 with profiling.maybeprofile(ui):
903 try:
905 try:
904 return cmdfunc()
906 return cmdfunc()
905 except error.SignatureError:
907 except error.SignatureError:
906 raise error.CommandError(cmd, _('invalid arguments'))
908 raise error.CommandError(cmd, _('invalid arguments'))
907
909
908 def _exceptionwarning(ui):
910 def _exceptionwarning(ui):
909 """Produce a warning message for the current active exception"""
911 """Produce a warning message for the current active exception"""
910
912
911 # For compatibility checking, we discard the portion of the hg
913 # For compatibility checking, we discard the portion of the hg
912 # version after the + on the assumption that if a "normal
914 # version after the + on the assumption that if a "normal
913 # user" is running a build with a + in it the packager
915 # user" is running a build with a + in it the packager
914 # probably built from fairly close to a tag and anyone with a
916 # probably built from fairly close to a tag and anyone with a
915 # 'make local' copy of hg (where the version number can be out
917 # 'make local' copy of hg (where the version number can be out
916 # of date) will be clueful enough to notice the implausible
918 # of date) will be clueful enough to notice the implausible
917 # version number and try updating.
919 # version number and try updating.
918 ct = util.versiontuple(n=2)
920 ct = util.versiontuple(n=2)
919 worst = None, ct, ''
921 worst = None, ct, ''
920 if ui.config('ui', 'supportcontact', None) is None:
922 if ui.config('ui', 'supportcontact', None) is None:
921 for name, mod in extensions.extensions():
923 for name, mod in extensions.extensions():
922 testedwith = getattr(mod, 'testedwith', '')
924 testedwith = getattr(mod, 'testedwith', '')
923 report = getattr(mod, 'buglink', _('the extension author.'))
925 report = getattr(mod, 'buglink', _('the extension author.'))
924 if not testedwith.strip():
926 if not testedwith.strip():
925 # We found an untested extension. It's likely the culprit.
927 # We found an untested extension. It's likely the culprit.
926 worst = name, 'unknown', report
928 worst = name, 'unknown', report
927 break
929 break
928
930
929 # Never blame on extensions bundled with Mercurial.
931 # Never blame on extensions bundled with Mercurial.
930 if testedwith == 'ships-with-hg-core':
932 if testedwith == 'ships-with-hg-core':
931 continue
933 continue
932
934
933 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
935 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
934 if ct in tested:
936 if ct in tested:
935 continue
937 continue
936
938
937 lower = [t for t in tested if t < ct]
939 lower = [t for t in tested if t < ct]
938 nearest = max(lower or tested)
940 nearest = max(lower or tested)
939 if worst[0] is None or nearest < worst[1]:
941 if worst[0] is None or nearest < worst[1]:
940 worst = name, nearest, report
942 worst = name, nearest, report
941 if worst[0] is not None:
943 if worst[0] is not None:
942 name, testedwith, report = worst
944 name, testedwith, report = worst
943 if not isinstance(testedwith, str):
945 if not isinstance(testedwith, str):
944 testedwith = '.'.join([str(c) for c in testedwith])
946 testedwith = '.'.join([str(c) for c in testedwith])
945 warning = (_('** Unknown exception encountered with '
947 warning = (_('** Unknown exception encountered with '
946 'possibly-broken third-party extension %s\n'
948 'possibly-broken third-party extension %s\n'
947 '** which supports versions %s of Mercurial.\n'
949 '** which supports versions %s of Mercurial.\n'
948 '** Please disable %s and try your action again.\n'
950 '** Please disable %s and try your action again.\n'
949 '** If that fixes the bug please report it to %s\n')
951 '** If that fixes the bug please report it to %s\n')
950 % (name, testedwith, name, report))
952 % (name, testedwith, name, report))
951 else:
953 else:
952 bugtracker = ui.config('ui', 'supportcontact', None)
954 bugtracker = ui.config('ui', 'supportcontact', None)
953 if bugtracker is None:
955 if bugtracker is None:
954 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
956 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
955 warning = (_("** unknown exception encountered, "
957 warning = (_("** unknown exception encountered, "
956 "please report by visiting\n** ") + bugtracker + '\n')
958 "please report by visiting\n** ") + bugtracker + '\n')
957 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
959 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
958 (_("** Mercurial Distributed SCM (version %s)\n") %
960 (_("** Mercurial Distributed SCM (version %s)\n") %
959 util.version()) +
961 util.version()) +
960 (_("** Extensions loaded: %s\n") %
962 (_("** Extensions loaded: %s\n") %
961 ", ".join([x[0] for x in extensions.extensions()])))
963 ", ".join([x[0] for x in extensions.extensions()])))
962 return warning
964 return warning
963
965
964 def handlecommandexception(ui):
966 def handlecommandexception(ui):
965 """Produce a warning message for broken commands
967 """Produce a warning message for broken commands
966
968
967 Called when handling an exception; the exception is reraised if
969 Called when handling an exception; the exception is reraised if
968 this function returns False, ignored otherwise.
970 this function returns False, ignored otherwise.
969 """
971 """
970 warning = _exceptionwarning(ui)
972 warning = _exceptionwarning(ui)
971 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
973 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
972 ui.warn(warning)
974 ui.warn(warning)
973 return False # re-raise the exception
975 return False # re-raise the exception
@@ -1,209 +1,223 b''
1 setup
1 setup
2 $ cat >> $HGRCPATH <<EOF
2 $ cat >> $HGRCPATH <<EOF
3 > [extensions]
3 > [extensions]
4 > blackbox=
4 > blackbox=
5 > mock=$TESTDIR/mockblackbox.py
5 > mock=$TESTDIR/mockblackbox.py
6 > mq=
6 > mq=
7 > [alias]
8 > confuse = log --limit 3
7 > EOF
9 > EOF
8 $ hg init blackboxtest
10 $ hg init blackboxtest
9 $ cd blackboxtest
11 $ cd blackboxtest
10
12
11 command, exit codes, and duration
13 command, exit codes, and duration
12
14
13 $ echo a > a
15 $ echo a > a
14 $ hg add a
16 $ hg add a
15 $ hg blackbox --config blackbox.dirty=True
17 $ hg blackbox --config blackbox.dirty=True
16 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a
18 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a
17 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob)
19 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob)
18 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox
20 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox
19
21
22 alias expansion is logged
23 $ hg confuse
24 $ hg blackbox
25 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a
26 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob)
27 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox
28 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox --config blackbox.dirty=True exited 0 after * seconds (glob)
29 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse
30 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'confuse' expands to 'log --limit 3'
31 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse exited 0 after * seconds (glob)
32 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
33
20 incoming change tracking
34 incoming change tracking
21
35
22 create two heads to verify that we only see one change in the log later
36 create two heads to verify that we only see one change in the log later
23 $ hg commit -ma
37 $ hg commit -ma
24 $ hg up null
38 $ hg up null
25 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
39 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
26 $ echo b > b
40 $ echo b > b
27 $ hg commit -Amb
41 $ hg commit -Amb
28 adding b
42 adding b
29 created new head
43 created new head
30
44
31 clone, commit, pull
45 clone, commit, pull
32 $ hg clone . ../blackboxtest2
46 $ hg clone . ../blackboxtest2
33 updating to branch default
47 updating to branch default
34 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 $ echo c > c
49 $ echo c > c
36 $ hg commit -Amc
50 $ hg commit -Amc
37 adding c
51 adding c
38 $ cd ../blackboxtest2
52 $ cd ../blackboxtest2
39 $ hg pull
53 $ hg pull
40 pulling from $TESTTMP/blackboxtest (glob)
54 pulling from $TESTTMP/blackboxtest (glob)
41 searching for changes
55 searching for changes
42 adding changesets
56 adding changesets
43 adding manifests
57 adding manifests
44 adding file changes
58 adding file changes
45 added 1 changesets with 1 changes to 1 files
59 added 1 changesets with 1 changes to 1 files
46 (run 'hg update' to get a working copy)
60 (run 'hg update' to get a working copy)
47 $ hg blackbox -l 6
61 $ hg blackbox -l 6
48 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull
62 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull
49 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated served branch cache in * seconds (glob)
63 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated served branch cache in * seconds (glob)
50 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote served branch cache with 1 labels and 2 nodes
64 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote served branch cache with 1 labels and 2 nodes
51 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> 1 incoming changes - new heads: d02f48003e62
65 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> 1 incoming changes - new heads: d02f48003e62
52 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull exited 0 after * seconds (glob)
66 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pull exited 0 after * seconds (glob)
53 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
67 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
54
68
55 we must not cause a failure if we cannot write to the log
69 we must not cause a failure if we cannot write to the log
56
70
57 $ hg rollback
71 $ hg rollback
58 repository tip rolled back to revision 1 (undo pull)
72 repository tip rolled back to revision 1 (undo pull)
59
73
60 $ mv .hg/blackbox.log .hg/blackbox.log-
74 $ mv .hg/blackbox.log .hg/blackbox.log-
61 $ mkdir .hg/blackbox.log
75 $ mkdir .hg/blackbox.log
62 $ hg --debug incoming
76 $ hg --debug incoming
63 warning: cannot write to blackbox.log: * (glob)
77 warning: cannot write to blackbox.log: * (glob)
64 comparing with $TESTTMP/blackboxtest (glob)
78 comparing with $TESTTMP/blackboxtest (glob)
65 query 1; heads
79 query 1; heads
66 searching for changes
80 searching for changes
67 all local heads known remotely
81 all local heads known remotely
68 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
82 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
69 tag: tip
83 tag: tip
70 phase: draft
84 phase: draft
71 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
85 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
72 parent: -1:0000000000000000000000000000000000000000
86 parent: -1:0000000000000000000000000000000000000000
73 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
87 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
74 user: test
88 user: test
75 date: Thu Jan 01 00:00:00 1970 +0000
89 date: Thu Jan 01 00:00:00 1970 +0000
76 files+: c
90 files+: c
77 extra: branch=default
91 extra: branch=default
78 description:
92 description:
79 c
93 c
80
94
81
95
82 $ hg pull
96 $ hg pull
83 pulling from $TESTTMP/blackboxtest (glob)
97 pulling from $TESTTMP/blackboxtest (glob)
84 searching for changes
98 searching for changes
85 adding changesets
99 adding changesets
86 adding manifests
100 adding manifests
87 adding file changes
101 adding file changes
88 added 1 changesets with 1 changes to 1 files
102 added 1 changesets with 1 changes to 1 files
89 (run 'hg update' to get a working copy)
103 (run 'hg update' to get a working copy)
90
104
91 a failure reading from the log is fatal
105 a failure reading from the log is fatal
92
106
93 $ hg blackbox -l 3
107 $ hg blackbox -l 3
94 abort: *$TESTTMP/blackboxtest2/.hg/blackbox.log* (glob)
108 abort: *$TESTTMP/blackboxtest2/.hg/blackbox.log* (glob)
95 [255]
109 [255]
96
110
97 $ rmdir .hg/blackbox.log
111 $ rmdir .hg/blackbox.log
98 $ mv .hg/blackbox.log- .hg/blackbox.log
112 $ mv .hg/blackbox.log- .hg/blackbox.log
99
113
100 backup bundles get logged
114 backup bundles get logged
101
115
102 $ touch d
116 $ touch d
103 $ hg commit -Amd
117 $ hg commit -Amd
104 adding d
118 adding d
105 created new head
119 created new head
106 $ hg strip tip
120 $ hg strip tip
107 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
121 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
108 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
122 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
109 $ hg blackbox -l 6
123 $ hg blackbox -l 6
110 1970/01/01 00:00:00 bob @73f6ee326b27d820b0472f1a825e3a50f3dc489b (5000)> strip tip
124 1970/01/01 00:00:00 bob @73f6ee326b27d820b0472f1a825e3a50f3dc489b (5000)> strip tip
111 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/73f6ee326b27-7612e004-backup.hg (glob)
125 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/73f6ee326b27-7612e004-backup.hg (glob)
112 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated base branch cache in * seconds (glob)
126 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> updated base branch cache in * seconds (glob)
113 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote base branch cache with 1 labels and 2 nodes
127 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> wrote base branch cache with 1 labels and 2 nodes
114 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> strip tip exited 0 after * seconds (glob)
128 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> strip tip exited 0 after * seconds (glob)
115 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
129 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> blackbox -l 6
116
130
117 extension and python hooks - use the eol extension for a pythonhook
131 extension and python hooks - use the eol extension for a pythonhook
118
132
119 $ echo '[extensions]' >> .hg/hgrc
133 $ echo '[extensions]' >> .hg/hgrc
120 $ echo 'eol=' >> .hg/hgrc
134 $ echo 'eol=' >> .hg/hgrc
121 $ echo '[hooks]' >> .hg/hgrc
135 $ echo '[hooks]' >> .hg/hgrc
122 $ echo 'update = echo hooked' >> .hg/hgrc
136 $ echo 'update = echo hooked' >> .hg/hgrc
123 $ hg update
137 $ hg update
124 hooked
138 hooked
125 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 1 other heads for branch "default"
140 1 other heads for branch "default"
127 $ hg blackbox -l 6
141 $ hg blackbox -l 6
128 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> update
142 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> update
129 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> writing .hg/cache/tags2-visible with 0 tags
143 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> writing .hg/cache/tags2-visible with 0 tags
130 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
144 1970/01/01 00:00:00 bob @6563da9dcf87b1949716e38ff3e3dfaa3198eb06 (5000)> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
131 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> exthook-update: echo hooked finished in * seconds (glob)
145 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> exthook-update: echo hooked finished in * seconds (glob)
132 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> update exited 0 after * seconds (glob)
146 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> update exited 0 after * seconds (glob)
133 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> blackbox -l 6
147 1970/01/01 00:00:00 bob @d02f48003e62c24e2659d97d30f2a83abe5d5d51 (5000)> blackbox -l 6
134
148
135 log rotation
149 log rotation
136
150
137 $ echo '[blackbox]' >> .hg/hgrc
151 $ echo '[blackbox]' >> .hg/hgrc
138 $ echo 'maxsize = 20 b' >> .hg/hgrc
152 $ echo 'maxsize = 20 b' >> .hg/hgrc
139 $ echo 'maxfiles = 3' >> .hg/hgrc
153 $ echo 'maxfiles = 3' >> .hg/hgrc
140 $ hg status
154 $ hg status
141 $ hg status
155 $ hg status
142 $ hg status
156 $ hg status
143 $ hg tip -q
157 $ hg tip -q
144 2:d02f48003e62
158 2:d02f48003e62
145 $ ls .hg/blackbox.log*
159 $ ls .hg/blackbox.log*
146 .hg/blackbox.log
160 .hg/blackbox.log
147 .hg/blackbox.log.1
161 .hg/blackbox.log.1
148 .hg/blackbox.log.2
162 .hg/blackbox.log.2
149 $ cd ..
163 $ cd ..
150
164
151 $ hg init blackboxtest3
165 $ hg init blackboxtest3
152 $ cd blackboxtest3
166 $ cd blackboxtest3
153 $ hg blackbox
167 $ hg blackbox
154 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
168 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox
155 $ mv .hg/blackbox.log .hg/blackbox.log-
169 $ mv .hg/blackbox.log .hg/blackbox.log-
156 $ mkdir .hg/blackbox.log
170 $ mkdir .hg/blackbox.log
157 $ sed -e 's/\(.*test1.*\)/#\1/; s#\(.*commit2.*\)#os.rmdir(".hg/blackbox.log")\
171 $ sed -e 's/\(.*test1.*\)/#\1/; s#\(.*commit2.*\)#os.rmdir(".hg/blackbox.log")\
158 > os.rename(".hg/blackbox.log-", ".hg/blackbox.log")\
172 > os.rename(".hg/blackbox.log-", ".hg/blackbox.log")\
159 > \1#' $TESTDIR/test-dispatch.py > ../test-dispatch.py
173 > \1#' $TESTDIR/test-dispatch.py > ../test-dispatch.py
160 $ python $TESTDIR/blackbox-readonly-dispatch.py
174 $ python $TESTDIR/blackbox-readonly-dispatch.py
161 running: add foo
175 running: add foo
162 result: 0
176 result: 0
163 running: commit -m commit1 -d 2000-01-01 foo
177 running: commit -m commit1 -d 2000-01-01 foo
164 result: None
178 result: None
165 running: commit -m commit2 -d 2000-01-02 foo
179 running: commit -m commit2 -d 2000-01-02 foo
166 result: None
180 result: None
167 running: log -r 0
181 running: log -r 0
168 changeset: 0:0e4634943879
182 changeset: 0:0e4634943879
169 user: test
183 user: test
170 date: Sat Jan 01 00:00:00 2000 +0000
184 date: Sat Jan 01 00:00:00 2000 +0000
171 summary: commit1
185 summary: commit1
172
186
173 result: None
187 result: None
174 running: log -r tip
188 running: log -r tip
175 changeset: 1:45589e459b2e
189 changeset: 1:45589e459b2e
176 tag: tip
190 tag: tip
177 user: test
191 user: test
178 date: Sun Jan 02 00:00:00 2000 +0000
192 date: Sun Jan 02 00:00:00 2000 +0000
179 summary: commit2
193 summary: commit2
180
194
181 result: None
195 result: None
182 $ hg blackbox
196 $ hg blackbox
183 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> commit -m commit2 -d 2000-01-02 foo
197 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> commit -m commit2 -d 2000-01-02 foo
184 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> updated served branch cache in * seconds (glob)
198 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> updated served branch cache in * seconds (glob)
185 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> wrote served branch cache with 1 labels and 1 nodes
199 1970/01/01 00:00:00 bob @0e46349438790c460c5c9f7546bfcd39b267bbd2 (5000)> wrote served branch cache with 1 labels and 1 nodes
186 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> commit -m commit2 -d 2000-01-02 foo exited 0 after * seconds (glob)
200 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> commit -m commit2 -d 2000-01-02 foo exited 0 after * seconds (glob)
187 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0
201 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0
188 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> writing .hg/cache/tags2-visible with 0 tags
202 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> writing .hg/cache/tags2-visible with 0 tags
189 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0 exited 0 after * seconds (glob)
203 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r 0 exited 0 after * seconds (glob)
190 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip
204 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip
191 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip exited 0 after * seconds (glob)
205 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> log -r tip exited 0 after * seconds (glob)
192 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> blackbox
206 1970/01/01 00:00:00 bob @45589e459b2edfbf3dbde7e01f611d2c1e7453d7 (5000)> blackbox
193
207
194 Test log recursion from dirty status check
208 Test log recursion from dirty status check
195
209
196 $ cat > ../r.py <<EOF
210 $ cat > ../r.py <<EOF
197 > from mercurial import context, error, extensions
211 > from mercurial import context, error, extensions
198 > x=[False]
212 > x=[False]
199 > def status(orig, *args, **opts):
213 > def status(orig, *args, **opts):
200 > args[0].repo().ui.log("broken", "recursion?")
214 > args[0].repo().ui.log("broken", "recursion?")
201 > return orig(*args, **opts)
215 > return orig(*args, **opts)
202 > def reposetup(ui, repo):
216 > def reposetup(ui, repo):
203 > extensions.wrapfunction(context.basectx, 'status', status)
217 > extensions.wrapfunction(context.basectx, 'status', status)
204 > EOF
218 > EOF
205 $ hg id --config extensions.x=../r.py --config blackbox.dirty=True
219 $ hg id --config extensions.x=../r.py --config blackbox.dirty=True
206 45589e459b2e tip
220 45589e459b2e tip
207
221
208 cleanup
222 cleanup
209 $ cd ..
223 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now