##// END OF EJS Templates
config: also respect HGRCSKIPREPO in `dispatch._getlocal`...
marmoute -
r44728:ef11dfc5 stable
parent child Browse files
Show More
@@ -1,1282 +1,1286 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 difflib
10 import difflib
11 import errno
11 import errno
12 import getopt
12 import getopt
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import signal
16 import signal
17 import sys
17 import sys
18 import traceback
18 import traceback
19
19
20
20
21 from .i18n import _
21 from .i18n import _
22 from .pycompat import getattr
22 from .pycompat import getattr
23
23
24 from hgdemandimport import tracing
24 from hgdemandimport import tracing
25
25
26 from . import (
26 from . import (
27 cmdutil,
27 cmdutil,
28 color,
28 color,
29 commands,
29 commands,
30 demandimport,
30 demandimport,
31 encoding,
31 encoding,
32 error,
32 error,
33 extensions,
33 extensions,
34 fancyopts,
34 fancyopts,
35 help,
35 help,
36 hg,
36 hg,
37 hook,
37 hook,
38 profiling,
38 profiling,
39 pycompat,
39 pycompat,
40 rcutil,
40 registrar,
41 registrar,
41 scmutil,
42 scmutil,
42 ui as uimod,
43 ui as uimod,
43 util,
44 util,
44 )
45 )
45
46
46 from .utils import (
47 from .utils import (
47 procutil,
48 procutil,
48 stringutil,
49 stringutil,
49 )
50 )
50
51
51
52
52 class request(object):
53 class request(object):
53 def __init__(
54 def __init__(
54 self,
55 self,
55 args,
56 args,
56 ui=None,
57 ui=None,
57 repo=None,
58 repo=None,
58 fin=None,
59 fin=None,
59 fout=None,
60 fout=None,
60 ferr=None,
61 ferr=None,
61 fmsg=None,
62 fmsg=None,
62 prereposetups=None,
63 prereposetups=None,
63 ):
64 ):
64 self.args = args
65 self.args = args
65 self.ui = ui
66 self.ui = ui
66 self.repo = repo
67 self.repo = repo
67
68
68 # input/output/error streams
69 # input/output/error streams
69 self.fin = fin
70 self.fin = fin
70 self.fout = fout
71 self.fout = fout
71 self.ferr = ferr
72 self.ferr = ferr
72 # separate stream for status/error messages
73 # separate stream for status/error messages
73 self.fmsg = fmsg
74 self.fmsg = fmsg
74
75
75 # remember options pre-parsed by _earlyparseopts()
76 # remember options pre-parsed by _earlyparseopts()
76 self.earlyoptions = {}
77 self.earlyoptions = {}
77
78
78 # reposetups which run before extensions, useful for chg to pre-fill
79 # reposetups which run before extensions, useful for chg to pre-fill
79 # low-level repo state (for example, changelog) before extensions.
80 # low-level repo state (for example, changelog) before extensions.
80 self.prereposetups = prereposetups or []
81 self.prereposetups = prereposetups or []
81
82
82 # store the parsed and canonical command
83 # store the parsed and canonical command
83 self.canonical_command = None
84 self.canonical_command = None
84
85
85 def _runexithandlers(self):
86 def _runexithandlers(self):
86 exc = None
87 exc = None
87 handlers = self.ui._exithandlers
88 handlers = self.ui._exithandlers
88 try:
89 try:
89 while handlers:
90 while handlers:
90 func, args, kwargs = handlers.pop()
91 func, args, kwargs = handlers.pop()
91 try:
92 try:
92 func(*args, **kwargs)
93 func(*args, **kwargs)
93 except: # re-raises below
94 except: # re-raises below
94 if exc is None:
95 if exc is None:
95 exc = sys.exc_info()[1]
96 exc = sys.exc_info()[1]
96 self.ui.warnnoi18n(b'error in exit handlers:\n')
97 self.ui.warnnoi18n(b'error in exit handlers:\n')
97 self.ui.traceback(force=True)
98 self.ui.traceback(force=True)
98 finally:
99 finally:
99 if exc is not None:
100 if exc is not None:
100 raise exc
101 raise exc
101
102
102
103
103 def run():
104 def run():
104 """run the command in sys.argv"""
105 """run the command in sys.argv"""
105 initstdio()
106 initstdio()
106 with tracing.log('parse args into request'):
107 with tracing.log('parse args into request'):
107 req = request(pycompat.sysargv[1:])
108 req = request(pycompat.sysargv[1:])
108 err = None
109 err = None
109 try:
110 try:
110 status = dispatch(req)
111 status = dispatch(req)
111 except error.StdioError as e:
112 except error.StdioError as e:
112 err = e
113 err = e
113 status = -1
114 status = -1
114
115
115 # In all cases we try to flush stdio streams.
116 # In all cases we try to flush stdio streams.
116 if util.safehasattr(req.ui, b'fout'):
117 if util.safehasattr(req.ui, b'fout'):
117 assert req.ui is not None # help pytype
118 assert req.ui is not None # help pytype
118 assert req.ui.fout is not None # help pytype
119 assert req.ui.fout is not None # help pytype
119 try:
120 try:
120 req.ui.fout.flush()
121 req.ui.fout.flush()
121 except IOError as e:
122 except IOError as e:
122 err = e
123 err = e
123 status = -1
124 status = -1
124
125
125 if util.safehasattr(req.ui, b'ferr'):
126 if util.safehasattr(req.ui, b'ferr'):
126 assert req.ui is not None # help pytype
127 assert req.ui is not None # help pytype
127 assert req.ui.ferr is not None # help pytype
128 assert req.ui.ferr is not None # help pytype
128 try:
129 try:
129 if err is not None and err.errno != errno.EPIPE:
130 if err is not None and err.errno != errno.EPIPE:
130 req.ui.ferr.write(
131 req.ui.ferr.write(
131 b'abort: %s\n' % encoding.strtolocal(err.strerror)
132 b'abort: %s\n' % encoding.strtolocal(err.strerror)
132 )
133 )
133 req.ui.ferr.flush()
134 req.ui.ferr.flush()
134 # There's not much we can do about an I/O error here. So (possibly)
135 # There's not much we can do about an I/O error here. So (possibly)
135 # change the status code and move on.
136 # change the status code and move on.
136 except IOError:
137 except IOError:
137 status = -1
138 status = -1
138
139
139 _silencestdio()
140 _silencestdio()
140 sys.exit(status & 255)
141 sys.exit(status & 255)
141
142
142
143
143 if pycompat.ispy3:
144 if pycompat.ispy3:
144
145
145 def initstdio():
146 def initstdio():
146 pass
147 pass
147
148
148 def _silencestdio():
149 def _silencestdio():
149 for fp in (sys.stdout, sys.stderr):
150 for fp in (sys.stdout, sys.stderr):
150 # Check if the file is okay
151 # Check if the file is okay
151 try:
152 try:
152 fp.flush()
153 fp.flush()
153 continue
154 continue
154 except IOError:
155 except IOError:
155 pass
156 pass
156 # Otherwise mark it as closed to silence "Exception ignored in"
157 # Otherwise mark it as closed to silence "Exception ignored in"
157 # message emitted by the interpreter finalizer. Be careful to
158 # message emitted by the interpreter finalizer. Be careful to
158 # not close procutil.stdout, which may be a fdopen-ed file object
159 # not close procutil.stdout, which may be a fdopen-ed file object
159 # and its close() actually closes the underlying file descriptor.
160 # and its close() actually closes the underlying file descriptor.
160 try:
161 try:
161 fp.close()
162 fp.close()
162 except IOError:
163 except IOError:
163 pass
164 pass
164
165
165
166
166 else:
167 else:
167
168
168 def initstdio():
169 def initstdio():
169 for fp in (sys.stdin, sys.stdout, sys.stderr):
170 for fp in (sys.stdin, sys.stdout, sys.stderr):
170 procutil.setbinary(fp)
171 procutil.setbinary(fp)
171
172
172 def _silencestdio():
173 def _silencestdio():
173 pass
174 pass
174
175
175
176
176 def _getsimilar(symbols, value):
177 def _getsimilar(symbols, value):
177 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
178 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
178 # The cutoff for similarity here is pretty arbitrary. It should
179 # The cutoff for similarity here is pretty arbitrary. It should
179 # probably be investigated and tweaked.
180 # probably be investigated and tweaked.
180 return [s for s in symbols if sim(s) > 0.6]
181 return [s for s in symbols if sim(s) > 0.6]
181
182
182
183
183 def _reportsimilar(write, similar):
184 def _reportsimilar(write, similar):
184 if len(similar) == 1:
185 if len(similar) == 1:
185 write(_(b"(did you mean %s?)\n") % similar[0])
186 write(_(b"(did you mean %s?)\n") % similar[0])
186 elif similar:
187 elif similar:
187 ss = b", ".join(sorted(similar))
188 ss = b", ".join(sorted(similar))
188 write(_(b"(did you mean one of %s?)\n") % ss)
189 write(_(b"(did you mean one of %s?)\n") % ss)
189
190
190
191
191 def _formatparse(write, inst):
192 def _formatparse(write, inst):
192 similar = []
193 similar = []
193 if isinstance(inst, error.UnknownIdentifier):
194 if isinstance(inst, error.UnknownIdentifier):
194 # make sure to check fileset first, as revset can invoke fileset
195 # make sure to check fileset first, as revset can invoke fileset
195 similar = _getsimilar(inst.symbols, inst.function)
196 similar = _getsimilar(inst.symbols, inst.function)
196 if len(inst.args) > 1:
197 if len(inst.args) > 1:
197 write(
198 write(
198 _(b"hg: parse error at %s: %s\n")
199 _(b"hg: parse error at %s: %s\n")
199 % (pycompat.bytestr(inst.args[1]), inst.args[0])
200 % (pycompat.bytestr(inst.args[1]), inst.args[0])
200 )
201 )
201 if inst.args[0].startswith(b' '):
202 if inst.args[0].startswith(b' '):
202 write(_(b"unexpected leading whitespace\n"))
203 write(_(b"unexpected leading whitespace\n"))
203 else:
204 else:
204 write(_(b"hg: parse error: %s\n") % inst.args[0])
205 write(_(b"hg: parse error: %s\n") % inst.args[0])
205 _reportsimilar(write, similar)
206 _reportsimilar(write, similar)
206 if inst.hint:
207 if inst.hint:
207 write(_(b"(%s)\n") % inst.hint)
208 write(_(b"(%s)\n") % inst.hint)
208
209
209
210
210 def _formatargs(args):
211 def _formatargs(args):
211 return b' '.join(procutil.shellquote(a) for a in args)
212 return b' '.join(procutil.shellquote(a) for a in args)
212
213
213
214
214 def dispatch(req):
215 def dispatch(req):
215 """run the command specified in req.args; returns an integer status code"""
216 """run the command specified in req.args; returns an integer status code"""
216 with tracing.log('dispatch.dispatch'):
217 with tracing.log('dispatch.dispatch'):
217 if req.ferr:
218 if req.ferr:
218 ferr = req.ferr
219 ferr = req.ferr
219 elif req.ui:
220 elif req.ui:
220 ferr = req.ui.ferr
221 ferr = req.ui.ferr
221 else:
222 else:
222 ferr = procutil.stderr
223 ferr = procutil.stderr
223
224
224 try:
225 try:
225 if not req.ui:
226 if not req.ui:
226 req.ui = uimod.ui.load()
227 req.ui = uimod.ui.load()
227 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
228 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
228 if req.earlyoptions[b'traceback']:
229 if req.earlyoptions[b'traceback']:
229 req.ui.setconfig(b'ui', b'traceback', b'on', b'--traceback')
230 req.ui.setconfig(b'ui', b'traceback', b'on', b'--traceback')
230
231
231 # set ui streams from the request
232 # set ui streams from the request
232 if req.fin:
233 if req.fin:
233 req.ui.fin = req.fin
234 req.ui.fin = req.fin
234 if req.fout:
235 if req.fout:
235 req.ui.fout = req.fout
236 req.ui.fout = req.fout
236 if req.ferr:
237 if req.ferr:
237 req.ui.ferr = req.ferr
238 req.ui.ferr = req.ferr
238 if req.fmsg:
239 if req.fmsg:
239 req.ui.fmsg = req.fmsg
240 req.ui.fmsg = req.fmsg
240 except error.Abort as inst:
241 except error.Abort as inst:
241 ferr.write(_(b"abort: %s\n") % inst)
242 ferr.write(_(b"abort: %s\n") % inst)
242 if inst.hint:
243 if inst.hint:
243 ferr.write(_(b"(%s)\n") % inst.hint)
244 ferr.write(_(b"(%s)\n") % inst.hint)
244 return -1
245 return -1
245 except error.ParseError as inst:
246 except error.ParseError as inst:
246 _formatparse(ferr.write, inst)
247 _formatparse(ferr.write, inst)
247 return -1
248 return -1
248
249
249 msg = _formatargs(req.args)
250 msg = _formatargs(req.args)
250 starttime = util.timer()
251 starttime = util.timer()
251 ret = 1 # default of Python exit code on unhandled exception
252 ret = 1 # default of Python exit code on unhandled exception
252 try:
253 try:
253 ret = _runcatch(req) or 0
254 ret = _runcatch(req) or 0
254 except error.ProgrammingError as inst:
255 except error.ProgrammingError as inst:
255 req.ui.error(_(b'** ProgrammingError: %s\n') % inst)
256 req.ui.error(_(b'** ProgrammingError: %s\n') % inst)
256 if inst.hint:
257 if inst.hint:
257 req.ui.error(_(b'** (%s)\n') % inst.hint)
258 req.ui.error(_(b'** (%s)\n') % inst.hint)
258 raise
259 raise
259 except KeyboardInterrupt as inst:
260 except KeyboardInterrupt as inst:
260 try:
261 try:
261 if isinstance(inst, error.SignalInterrupt):
262 if isinstance(inst, error.SignalInterrupt):
262 msg = _(b"killed!\n")
263 msg = _(b"killed!\n")
263 else:
264 else:
264 msg = _(b"interrupted!\n")
265 msg = _(b"interrupted!\n")
265 req.ui.error(msg)
266 req.ui.error(msg)
266 except error.SignalInterrupt:
267 except error.SignalInterrupt:
267 # maybe pager would quit without consuming all the output, and
268 # maybe pager would quit without consuming all the output, and
268 # SIGPIPE was raised. we cannot print anything in this case.
269 # SIGPIPE was raised. we cannot print anything in this case.
269 pass
270 pass
270 except IOError as inst:
271 except IOError as inst:
271 if inst.errno != errno.EPIPE:
272 if inst.errno != errno.EPIPE:
272 raise
273 raise
273 ret = -1
274 ret = -1
274 finally:
275 finally:
275 duration = util.timer() - starttime
276 duration = util.timer() - starttime
276 req.ui.flush()
277 req.ui.flush()
277 if req.ui.logblockedtimes:
278 if req.ui.logblockedtimes:
278 req.ui._blockedtimes[b'command_duration'] = duration * 1000
279 req.ui._blockedtimes[b'command_duration'] = duration * 1000
279 req.ui.log(
280 req.ui.log(
280 b'uiblocked',
281 b'uiblocked',
281 b'ui blocked ms\n',
282 b'ui blocked ms\n',
282 **pycompat.strkwargs(req.ui._blockedtimes)
283 **pycompat.strkwargs(req.ui._blockedtimes)
283 )
284 )
284 return_code = ret & 255
285 return_code = ret & 255
285 req.ui.log(
286 req.ui.log(
286 b"commandfinish",
287 b"commandfinish",
287 b"%s exited %d after %0.2f seconds\n",
288 b"%s exited %d after %0.2f seconds\n",
288 msg,
289 msg,
289 return_code,
290 return_code,
290 duration,
291 duration,
291 return_code=return_code,
292 return_code=return_code,
292 duration=duration,
293 duration=duration,
293 canonical_command=req.canonical_command,
294 canonical_command=req.canonical_command,
294 )
295 )
295 try:
296 try:
296 req._runexithandlers()
297 req._runexithandlers()
297 except: # exiting, so no re-raises
298 except: # exiting, so no re-raises
298 ret = ret or -1
299 ret = ret or -1
299 return ret
300 return ret
300
301
301
302
302 def _runcatch(req):
303 def _runcatch(req):
303 with tracing.log('dispatch._runcatch'):
304 with tracing.log('dispatch._runcatch'):
304
305
305 def catchterm(*args):
306 def catchterm(*args):
306 raise error.SignalInterrupt
307 raise error.SignalInterrupt
307
308
308 ui = req.ui
309 ui = req.ui
309 try:
310 try:
310 for name in b'SIGBREAK', b'SIGHUP', b'SIGTERM':
311 for name in b'SIGBREAK', b'SIGHUP', b'SIGTERM':
311 num = getattr(signal, name, None)
312 num = getattr(signal, name, None)
312 if num:
313 if num:
313 signal.signal(num, catchterm)
314 signal.signal(num, catchterm)
314 except ValueError:
315 except ValueError:
315 pass # happens if called in a thread
316 pass # happens if called in a thread
316
317
317 def _runcatchfunc():
318 def _runcatchfunc():
318 realcmd = None
319 realcmd = None
319 try:
320 try:
320 cmdargs = fancyopts.fancyopts(
321 cmdargs = fancyopts.fancyopts(
321 req.args[:], commands.globalopts, {}
322 req.args[:], commands.globalopts, {}
322 )
323 )
323 cmd = cmdargs[0]
324 cmd = cmdargs[0]
324 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
325 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
325 realcmd = aliases[0]
326 realcmd = aliases[0]
326 except (
327 except (
327 error.UnknownCommand,
328 error.UnknownCommand,
328 error.AmbiguousCommand,
329 error.AmbiguousCommand,
329 IndexError,
330 IndexError,
330 getopt.GetoptError,
331 getopt.GetoptError,
331 ):
332 ):
332 # Don't handle this here. We know the command is
333 # Don't handle this here. We know the command is
333 # invalid, but all we're worried about for now is that
334 # invalid, but all we're worried about for now is that
334 # it's not a command that server operators expect to
335 # it's not a command that server operators expect to
335 # be safe to offer to users in a sandbox.
336 # be safe to offer to users in a sandbox.
336 pass
337 pass
337 if realcmd == b'serve' and b'--stdio' in cmdargs:
338 if realcmd == b'serve' and b'--stdio' in cmdargs:
338 # We want to constrain 'hg serve --stdio' instances pretty
339 # We want to constrain 'hg serve --stdio' instances pretty
339 # closely, as many shared-ssh access tools want to grant
340 # closely, as many shared-ssh access tools want to grant
340 # access to run *only* 'hg -R $repo serve --stdio'. We
341 # access to run *only* 'hg -R $repo serve --stdio'. We
341 # restrict to exactly that set of arguments, and prohibit
342 # restrict to exactly that set of arguments, and prohibit
342 # any repo name that starts with '--' to prevent
343 # any repo name that starts with '--' to prevent
343 # shenanigans wherein a user does something like pass
344 # shenanigans wherein a user does something like pass
344 # --debugger or --config=ui.debugger=1 as a repo
345 # --debugger or --config=ui.debugger=1 as a repo
345 # name. This used to actually run the debugger.
346 # name. This used to actually run the debugger.
346 if (
347 if (
347 len(req.args) != 4
348 len(req.args) != 4
348 or req.args[0] != b'-R'
349 or req.args[0] != b'-R'
349 or req.args[1].startswith(b'--')
350 or req.args[1].startswith(b'--')
350 or req.args[2] != b'serve'
351 or req.args[2] != b'serve'
351 or req.args[3] != b'--stdio'
352 or req.args[3] != b'--stdio'
352 ):
353 ):
353 raise error.Abort(
354 raise error.Abort(
354 _(b'potentially unsafe serve --stdio invocation: %s')
355 _(b'potentially unsafe serve --stdio invocation: %s')
355 % (stringutil.pprint(req.args),)
356 % (stringutil.pprint(req.args),)
356 )
357 )
357
358
358 try:
359 try:
359 debugger = b'pdb'
360 debugger = b'pdb'
360 debugtrace = {b'pdb': pdb.set_trace}
361 debugtrace = {b'pdb': pdb.set_trace}
361 debugmortem = {b'pdb': pdb.post_mortem}
362 debugmortem = {b'pdb': pdb.post_mortem}
362
363
363 # read --config before doing anything else
364 # read --config before doing anything else
364 # (e.g. to change trust settings for reading .hg/hgrc)
365 # (e.g. to change trust settings for reading .hg/hgrc)
365 cfgs = _parseconfig(req.ui, req.earlyoptions[b'config'])
366 cfgs = _parseconfig(req.ui, req.earlyoptions[b'config'])
366
367
367 if req.repo:
368 if req.repo:
368 # copy configs that were passed on the cmdline (--config) to
369 # copy configs that were passed on the cmdline (--config) to
369 # the repo ui
370 # the repo ui
370 for sec, name, val in cfgs:
371 for sec, name, val in cfgs:
371 req.repo.ui.setconfig(
372 req.repo.ui.setconfig(
372 sec, name, val, source=b'--config'
373 sec, name, val, source=b'--config'
373 )
374 )
374
375
375 # developer config: ui.debugger
376 # developer config: ui.debugger
376 debugger = ui.config(b"ui", b"debugger")
377 debugger = ui.config(b"ui", b"debugger")
377 debugmod = pdb
378 debugmod = pdb
378 if not debugger or ui.plain():
379 if not debugger or ui.plain():
379 # if we are in HGPLAIN mode, then disable custom debugging
380 # if we are in HGPLAIN mode, then disable custom debugging
380 debugger = b'pdb'
381 debugger = b'pdb'
381 elif req.earlyoptions[b'debugger']:
382 elif req.earlyoptions[b'debugger']:
382 # This import can be slow for fancy debuggers, so only
383 # This import can be slow for fancy debuggers, so only
383 # do it when absolutely necessary, i.e. when actual
384 # do it when absolutely necessary, i.e. when actual
384 # debugging has been requested
385 # debugging has been requested
385 with demandimport.deactivated():
386 with demandimport.deactivated():
386 try:
387 try:
387 debugmod = __import__(debugger)
388 debugmod = __import__(debugger)
388 except ImportError:
389 except ImportError:
389 pass # Leave debugmod = pdb
390 pass # Leave debugmod = pdb
390
391
391 debugtrace[debugger] = debugmod.set_trace
392 debugtrace[debugger] = debugmod.set_trace
392 debugmortem[debugger] = debugmod.post_mortem
393 debugmortem[debugger] = debugmod.post_mortem
393
394
394 # enter the debugger before command execution
395 # enter the debugger before command execution
395 if req.earlyoptions[b'debugger']:
396 if req.earlyoptions[b'debugger']:
396 ui.warn(
397 ui.warn(
397 _(
398 _(
398 b"entering debugger - "
399 b"entering debugger - "
399 b"type c to continue starting hg or h for help\n"
400 b"type c to continue starting hg or h for help\n"
400 )
401 )
401 )
402 )
402
403
403 if (
404 if (
404 debugger != b'pdb'
405 debugger != b'pdb'
405 and debugtrace[debugger] == debugtrace[b'pdb']
406 and debugtrace[debugger] == debugtrace[b'pdb']
406 ):
407 ):
407 ui.warn(
408 ui.warn(
408 _(
409 _(
409 b"%s debugger specified "
410 b"%s debugger specified "
410 b"but its module was not found\n"
411 b"but its module was not found\n"
411 )
412 )
412 % debugger
413 % debugger
413 )
414 )
414 with demandimport.deactivated():
415 with demandimport.deactivated():
415 debugtrace[debugger]()
416 debugtrace[debugger]()
416 try:
417 try:
417 return _dispatch(req)
418 return _dispatch(req)
418 finally:
419 finally:
419 ui.flush()
420 ui.flush()
420 except: # re-raises
421 except: # re-raises
421 # enter the debugger when we hit an exception
422 # enter the debugger when we hit an exception
422 if req.earlyoptions[b'debugger']:
423 if req.earlyoptions[b'debugger']:
423 traceback.print_exc()
424 traceback.print_exc()
424 debugmortem[debugger](sys.exc_info()[2])
425 debugmortem[debugger](sys.exc_info()[2])
425 raise
426 raise
426
427
427 return _callcatch(ui, _runcatchfunc)
428 return _callcatch(ui, _runcatchfunc)
428
429
429
430
430 def _callcatch(ui, func):
431 def _callcatch(ui, func):
431 """like scmutil.callcatch but handles more high-level exceptions about
432 """like scmutil.callcatch but handles more high-level exceptions about
432 config parsing and commands. besides, use handlecommandexception to handle
433 config parsing and commands. besides, use handlecommandexception to handle
433 uncaught exceptions.
434 uncaught exceptions.
434 """
435 """
435 try:
436 try:
436 return scmutil.callcatch(ui, func)
437 return scmutil.callcatch(ui, func)
437 except error.AmbiguousCommand as inst:
438 except error.AmbiguousCommand as inst:
438 ui.warn(
439 ui.warn(
439 _(b"hg: command '%s' is ambiguous:\n %s\n")
440 _(b"hg: command '%s' is ambiguous:\n %s\n")
440 % (inst.args[0], b" ".join(inst.args[1]))
441 % (inst.args[0], b" ".join(inst.args[1]))
441 )
442 )
442 except error.CommandError as inst:
443 except error.CommandError as inst:
443 if inst.args[0]:
444 if inst.args[0]:
444 ui.pager(b'help')
445 ui.pager(b'help')
445 msgbytes = pycompat.bytestr(inst.args[1])
446 msgbytes = pycompat.bytestr(inst.args[1])
446 ui.warn(_(b"hg %s: %s\n") % (inst.args[0], msgbytes))
447 ui.warn(_(b"hg %s: %s\n") % (inst.args[0], msgbytes))
447 commands.help_(ui, inst.args[0], full=False, command=True)
448 commands.help_(ui, inst.args[0], full=False, command=True)
448 else:
449 else:
449 ui.warn(_(b"hg: %s\n") % inst.args[1])
450 ui.warn(_(b"hg: %s\n") % inst.args[1])
450 ui.warn(_(b"(use 'hg help -v' for a list of global options)\n"))
451 ui.warn(_(b"(use 'hg help -v' for a list of global options)\n"))
451 except error.ParseError as inst:
452 except error.ParseError as inst:
452 _formatparse(ui.warn, inst)
453 _formatparse(ui.warn, inst)
453 return -1
454 return -1
454 except error.UnknownCommand as inst:
455 except error.UnknownCommand as inst:
455 nocmdmsg = _(b"hg: unknown command '%s'\n") % inst.args[0]
456 nocmdmsg = _(b"hg: unknown command '%s'\n") % inst.args[0]
456 try:
457 try:
457 # check if the command is in a disabled extension
458 # check if the command is in a disabled extension
458 # (but don't check for extensions themselves)
459 # (but don't check for extensions themselves)
459 formatted = help.formattedhelp(
460 formatted = help.formattedhelp(
460 ui, commands, inst.args[0], unknowncmd=True
461 ui, commands, inst.args[0], unknowncmd=True
461 )
462 )
462 ui.warn(nocmdmsg)
463 ui.warn(nocmdmsg)
463 ui.write(formatted)
464 ui.write(formatted)
464 except (error.UnknownCommand, error.Abort):
465 except (error.UnknownCommand, error.Abort):
465 suggested = False
466 suggested = False
466 if len(inst.args) == 2:
467 if len(inst.args) == 2:
467 sim = _getsimilar(inst.args[1], inst.args[0])
468 sim = _getsimilar(inst.args[1], inst.args[0])
468 if sim:
469 if sim:
469 ui.warn(nocmdmsg)
470 ui.warn(nocmdmsg)
470 _reportsimilar(ui.warn, sim)
471 _reportsimilar(ui.warn, sim)
471 suggested = True
472 suggested = True
472 if not suggested:
473 if not suggested:
473 ui.warn(nocmdmsg)
474 ui.warn(nocmdmsg)
474 ui.warn(_(b"(use 'hg help' for a list of commands)\n"))
475 ui.warn(_(b"(use 'hg help' for a list of commands)\n"))
475 except IOError:
476 except IOError:
476 raise
477 raise
477 except KeyboardInterrupt:
478 except KeyboardInterrupt:
478 raise
479 raise
479 except: # probably re-raises
480 except: # probably re-raises
480 if not handlecommandexception(ui):
481 if not handlecommandexception(ui):
481 raise
482 raise
482
483
483 return -1
484 return -1
484
485
485
486
486 def aliasargs(fn, givenargs):
487 def aliasargs(fn, givenargs):
487 args = []
488 args = []
488 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
489 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
489 if not util.safehasattr(fn, b'_origfunc'):
490 if not util.safehasattr(fn, b'_origfunc'):
490 args = getattr(fn, 'args', args)
491 args = getattr(fn, 'args', args)
491 if args:
492 if args:
492 cmd = b' '.join(map(procutil.shellquote, args))
493 cmd = b' '.join(map(procutil.shellquote, args))
493
494
494 nums = []
495 nums = []
495
496
496 def replacer(m):
497 def replacer(m):
497 num = int(m.group(1)) - 1
498 num = int(m.group(1)) - 1
498 nums.append(num)
499 nums.append(num)
499 if num < len(givenargs):
500 if num < len(givenargs):
500 return givenargs[num]
501 return givenargs[num]
501 raise error.Abort(_(b'too few arguments for command alias'))
502 raise error.Abort(_(b'too few arguments for command alias'))
502
503
503 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
504 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
504 givenargs = [x for i, x in enumerate(givenargs) if i not in nums]
505 givenargs = [x for i, x in enumerate(givenargs) if i not in nums]
505 args = pycompat.shlexsplit(cmd)
506 args = pycompat.shlexsplit(cmd)
506 return args + givenargs
507 return args + givenargs
507
508
508
509
509 def aliasinterpolate(name, args, cmd):
510 def aliasinterpolate(name, args, cmd):
510 '''interpolate args into cmd for shell aliases
511 '''interpolate args into cmd for shell aliases
511
512
512 This also handles $0, $@ and "$@".
513 This also handles $0, $@ and "$@".
513 '''
514 '''
514 # util.interpolate can't deal with "$@" (with quotes) because it's only
515 # util.interpolate can't deal with "$@" (with quotes) because it's only
515 # built to match prefix + patterns.
516 # built to match prefix + patterns.
516 replacemap = dict((b'$%d' % (i + 1), arg) for i, arg in enumerate(args))
517 replacemap = dict((b'$%d' % (i + 1), arg) for i, arg in enumerate(args))
517 replacemap[b'$0'] = name
518 replacemap[b'$0'] = name
518 replacemap[b'$$'] = b'$'
519 replacemap[b'$$'] = b'$'
519 replacemap[b'$@'] = b' '.join(args)
520 replacemap[b'$@'] = b' '.join(args)
520 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
521 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
521 # parameters, separated out into words. Emulate the same behavior here by
522 # parameters, separated out into words. Emulate the same behavior here by
522 # quoting the arguments individually. POSIX shells will then typically
523 # quoting the arguments individually. POSIX shells will then typically
523 # tokenize each argument into exactly one word.
524 # tokenize each argument into exactly one word.
524 replacemap[b'"$@"'] = b' '.join(procutil.shellquote(arg) for arg in args)
525 replacemap[b'"$@"'] = b' '.join(procutil.shellquote(arg) for arg in args)
525 # escape '\$' for regex
526 # escape '\$' for regex
526 regex = b'|'.join(replacemap.keys()).replace(b'$', br'\$')
527 regex = b'|'.join(replacemap.keys()).replace(b'$', br'\$')
527 r = re.compile(regex)
528 r = re.compile(regex)
528 return r.sub(lambda x: replacemap[x.group()], cmd)
529 return r.sub(lambda x: replacemap[x.group()], cmd)
529
530
530
531
531 class cmdalias(object):
532 class cmdalias(object):
532 def __init__(self, ui, name, definition, cmdtable, source):
533 def __init__(self, ui, name, definition, cmdtable, source):
533 self.name = self.cmd = name
534 self.name = self.cmd = name
534 self.cmdname = b''
535 self.cmdname = b''
535 self.definition = definition
536 self.definition = definition
536 self.fn = None
537 self.fn = None
537 self.givenargs = []
538 self.givenargs = []
538 self.opts = []
539 self.opts = []
539 self.help = b''
540 self.help = b''
540 self.badalias = None
541 self.badalias = None
541 self.unknowncmd = False
542 self.unknowncmd = False
542 self.source = source
543 self.source = source
543
544
544 try:
545 try:
545 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
546 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
546 for alias, e in pycompat.iteritems(cmdtable):
547 for alias, e in pycompat.iteritems(cmdtable):
547 if e is entry:
548 if e is entry:
548 self.cmd = alias
549 self.cmd = alias
549 break
550 break
550 self.shadows = True
551 self.shadows = True
551 except error.UnknownCommand:
552 except error.UnknownCommand:
552 self.shadows = False
553 self.shadows = False
553
554
554 if not self.definition:
555 if not self.definition:
555 self.badalias = _(b"no definition for alias '%s'") % self.name
556 self.badalias = _(b"no definition for alias '%s'") % self.name
556 return
557 return
557
558
558 if self.definition.startswith(b'!'):
559 if self.definition.startswith(b'!'):
559 shdef = self.definition[1:]
560 shdef = self.definition[1:]
560 self.shell = True
561 self.shell = True
561
562
562 def fn(ui, *args):
563 def fn(ui, *args):
563 env = {b'HG_ARGS': b' '.join((self.name,) + args)}
564 env = {b'HG_ARGS': b' '.join((self.name,) + args)}
564
565
565 def _checkvar(m):
566 def _checkvar(m):
566 if m.groups()[0] == b'$':
567 if m.groups()[0] == b'$':
567 return m.group()
568 return m.group()
568 elif int(m.groups()[0]) <= len(args):
569 elif int(m.groups()[0]) <= len(args):
569 return m.group()
570 return m.group()
570 else:
571 else:
571 ui.debug(
572 ui.debug(
572 b"No argument found for substitution "
573 b"No argument found for substitution "
573 b"of %i variable in alias '%s' definition.\n"
574 b"of %i variable in alias '%s' definition.\n"
574 % (int(m.groups()[0]), self.name)
575 % (int(m.groups()[0]), self.name)
575 )
576 )
576 return b''
577 return b''
577
578
578 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
579 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
579 cmd = aliasinterpolate(self.name, args, cmd)
580 cmd = aliasinterpolate(self.name, args, cmd)
580 return ui.system(
581 return ui.system(
581 cmd, environ=env, blockedtag=b'alias_%s' % self.name
582 cmd, environ=env, blockedtag=b'alias_%s' % self.name
582 )
583 )
583
584
584 self.fn = fn
585 self.fn = fn
585 self.alias = True
586 self.alias = True
586 self._populatehelp(ui, name, shdef, self.fn)
587 self._populatehelp(ui, name, shdef, self.fn)
587 return
588 return
588
589
589 try:
590 try:
590 args = pycompat.shlexsplit(self.definition)
591 args = pycompat.shlexsplit(self.definition)
591 except ValueError as inst:
592 except ValueError as inst:
592 self.badalias = _(b"error in definition for alias '%s': %s") % (
593 self.badalias = _(b"error in definition for alias '%s': %s") % (
593 self.name,
594 self.name,
594 stringutil.forcebytestr(inst),
595 stringutil.forcebytestr(inst),
595 )
596 )
596 return
597 return
597 earlyopts, args = _earlysplitopts(args)
598 earlyopts, args = _earlysplitopts(args)
598 if earlyopts:
599 if earlyopts:
599 self.badalias = _(
600 self.badalias = _(
600 b"error in definition for alias '%s': %s may "
601 b"error in definition for alias '%s': %s may "
601 b"only be given on the command line"
602 b"only be given on the command line"
602 ) % (self.name, b'/'.join(pycompat.ziplist(*earlyopts)[0]))
603 ) % (self.name, b'/'.join(pycompat.ziplist(*earlyopts)[0]))
603 return
604 return
604 self.cmdname = cmd = args.pop(0)
605 self.cmdname = cmd = args.pop(0)
605 self.givenargs = args
606 self.givenargs = args
606
607
607 try:
608 try:
608 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
609 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
609 if len(tableentry) > 2:
610 if len(tableentry) > 2:
610 self.fn, self.opts, cmdhelp = tableentry
611 self.fn, self.opts, cmdhelp = tableentry
611 else:
612 else:
612 self.fn, self.opts = tableentry
613 self.fn, self.opts = tableentry
613 cmdhelp = None
614 cmdhelp = None
614
615
615 self.alias = True
616 self.alias = True
616 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
617 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
617
618
618 except error.UnknownCommand:
619 except error.UnknownCommand:
619 self.badalias = _(
620 self.badalias = _(
620 b"alias '%s' resolves to unknown command '%s'"
621 b"alias '%s' resolves to unknown command '%s'"
621 ) % (self.name, cmd,)
622 ) % (self.name, cmd,)
622 self.unknowncmd = True
623 self.unknowncmd = True
623 except error.AmbiguousCommand:
624 except error.AmbiguousCommand:
624 self.badalias = _(
625 self.badalias = _(
625 b"alias '%s' resolves to ambiguous command '%s'"
626 b"alias '%s' resolves to ambiguous command '%s'"
626 ) % (self.name, cmd)
627 ) % (self.name, cmd)
627
628
628 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
629 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
629 # confine strings to be passed to i18n.gettext()
630 # confine strings to be passed to i18n.gettext()
630 cfg = {}
631 cfg = {}
631 for k in (b'doc', b'help', b'category'):
632 for k in (b'doc', b'help', b'category'):
632 v = ui.config(b'alias', b'%s:%s' % (name, k), None)
633 v = ui.config(b'alias', b'%s:%s' % (name, k), None)
633 if v is None:
634 if v is None:
634 continue
635 continue
635 if not encoding.isasciistr(v):
636 if not encoding.isasciistr(v):
636 self.badalias = _(
637 self.badalias = _(
637 b"non-ASCII character in alias definition '%s:%s'"
638 b"non-ASCII character in alias definition '%s:%s'"
638 ) % (name, k)
639 ) % (name, k)
639 return
640 return
640 cfg[k] = v
641 cfg[k] = v
641
642
642 self.help = cfg.get(b'help', defaulthelp or b'')
643 self.help = cfg.get(b'help', defaulthelp or b'')
643 if self.help and self.help.startswith(b"hg " + cmd):
644 if self.help and self.help.startswith(b"hg " + cmd):
644 # drop prefix in old-style help lines so hg shows the alias
645 # drop prefix in old-style help lines so hg shows the alias
645 self.help = self.help[4 + len(cmd) :]
646 self.help = self.help[4 + len(cmd) :]
646
647
647 self.owndoc = b'doc' in cfg
648 self.owndoc = b'doc' in cfg
648 doc = cfg.get(b'doc', pycompat.getdoc(fn))
649 doc = cfg.get(b'doc', pycompat.getdoc(fn))
649 if doc is not None:
650 if doc is not None:
650 doc = pycompat.sysstr(doc)
651 doc = pycompat.sysstr(doc)
651 self.__doc__ = doc
652 self.__doc__ = doc
652
653
653 self.helpcategory = cfg.get(
654 self.helpcategory = cfg.get(
654 b'category', registrar.command.CATEGORY_NONE
655 b'category', registrar.command.CATEGORY_NONE
655 )
656 )
656
657
657 @property
658 @property
658 def args(self):
659 def args(self):
659 args = pycompat.maplist(util.expandpath, self.givenargs)
660 args = pycompat.maplist(util.expandpath, self.givenargs)
660 return aliasargs(self.fn, args)
661 return aliasargs(self.fn, args)
661
662
662 def __getattr__(self, name):
663 def __getattr__(self, name):
663 adefaults = {
664 adefaults = {
664 'norepo': True,
665 'norepo': True,
665 'intents': set(),
666 'intents': set(),
666 'optionalrepo': False,
667 'optionalrepo': False,
667 'inferrepo': False,
668 'inferrepo': False,
668 }
669 }
669 if name not in adefaults:
670 if name not in adefaults:
670 raise AttributeError(name)
671 raise AttributeError(name)
671 if self.badalias or util.safehasattr(self, b'shell'):
672 if self.badalias or util.safehasattr(self, b'shell'):
672 return adefaults[name]
673 return adefaults[name]
673 return getattr(self.fn, name)
674 return getattr(self.fn, name)
674
675
675 def __call__(self, ui, *args, **opts):
676 def __call__(self, ui, *args, **opts):
676 if self.badalias:
677 if self.badalias:
677 hint = None
678 hint = None
678 if self.unknowncmd:
679 if self.unknowncmd:
679 try:
680 try:
680 # check if the command is in a disabled extension
681 # check if the command is in a disabled extension
681 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
682 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
682 hint = _(b"'%s' is provided by '%s' extension") % (cmd, ext)
683 hint = _(b"'%s' is provided by '%s' extension") % (cmd, ext)
683 except error.UnknownCommand:
684 except error.UnknownCommand:
684 pass
685 pass
685 raise error.Abort(self.badalias, hint=hint)
686 raise error.Abort(self.badalias, hint=hint)
686 if self.shadows:
687 if self.shadows:
687 ui.debug(
688 ui.debug(
688 b"alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)
689 b"alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)
689 )
690 )
690
691
691 ui.log(
692 ui.log(
692 b'commandalias',
693 b'commandalias',
693 b"alias '%s' expands to '%s'\n",
694 b"alias '%s' expands to '%s'\n",
694 self.name,
695 self.name,
695 self.definition,
696 self.definition,
696 )
697 )
697 if util.safehasattr(self, b'shell'):
698 if util.safehasattr(self, b'shell'):
698 return self.fn(ui, *args, **opts)
699 return self.fn(ui, *args, **opts)
699 else:
700 else:
700 try:
701 try:
701 return util.checksignature(self.fn)(ui, *args, **opts)
702 return util.checksignature(self.fn)(ui, *args, **opts)
702 except error.SignatureError:
703 except error.SignatureError:
703 args = b' '.join([self.cmdname] + self.args)
704 args = b' '.join([self.cmdname] + self.args)
704 ui.debug(b"alias '%s' expands to '%s'\n" % (self.name, args))
705 ui.debug(b"alias '%s' expands to '%s'\n" % (self.name, args))
705 raise
706 raise
706
707
707
708
708 class lazyaliasentry(object):
709 class lazyaliasentry(object):
709 """like a typical command entry (func, opts, help), but is lazy"""
710 """like a typical command entry (func, opts, help), but is lazy"""
710
711
711 def __init__(self, ui, name, definition, cmdtable, source):
712 def __init__(self, ui, name, definition, cmdtable, source):
712 self.ui = ui
713 self.ui = ui
713 self.name = name
714 self.name = name
714 self.definition = definition
715 self.definition = definition
715 self.cmdtable = cmdtable.copy()
716 self.cmdtable = cmdtable.copy()
716 self.source = source
717 self.source = source
717 self.alias = True
718 self.alias = True
718
719
719 @util.propertycache
720 @util.propertycache
720 def _aliasdef(self):
721 def _aliasdef(self):
721 return cmdalias(
722 return cmdalias(
722 self.ui, self.name, self.definition, self.cmdtable, self.source
723 self.ui, self.name, self.definition, self.cmdtable, self.source
723 )
724 )
724
725
725 def __getitem__(self, n):
726 def __getitem__(self, n):
726 aliasdef = self._aliasdef
727 aliasdef = self._aliasdef
727 if n == 0:
728 if n == 0:
728 return aliasdef
729 return aliasdef
729 elif n == 1:
730 elif n == 1:
730 return aliasdef.opts
731 return aliasdef.opts
731 elif n == 2:
732 elif n == 2:
732 return aliasdef.help
733 return aliasdef.help
733 else:
734 else:
734 raise IndexError
735 raise IndexError
735
736
736 def __iter__(self):
737 def __iter__(self):
737 for i in range(3):
738 for i in range(3):
738 yield self[i]
739 yield self[i]
739
740
740 def __len__(self):
741 def __len__(self):
741 return 3
742 return 3
742
743
743
744
744 def addaliases(ui, cmdtable):
745 def addaliases(ui, cmdtable):
745 # aliases are processed after extensions have been loaded, so they
746 # aliases are processed after extensions have been loaded, so they
746 # may use extension commands. Aliases can also use other alias definitions,
747 # may use extension commands. Aliases can also use other alias definitions,
747 # but only if they have been defined prior to the current definition.
748 # but only if they have been defined prior to the current definition.
748 for alias, definition in ui.configitems(b'alias', ignoresub=True):
749 for alias, definition in ui.configitems(b'alias', ignoresub=True):
749 try:
750 try:
750 if cmdtable[alias].definition == definition:
751 if cmdtable[alias].definition == definition:
751 continue
752 continue
752 except (KeyError, AttributeError):
753 except (KeyError, AttributeError):
753 # definition might not exist or it might not be a cmdalias
754 # definition might not exist or it might not be a cmdalias
754 pass
755 pass
755
756
756 source = ui.configsource(b'alias', alias)
757 source = ui.configsource(b'alias', alias)
757 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
758 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
758 cmdtable[alias] = entry
759 cmdtable[alias] = entry
759
760
760
761
761 def _parse(ui, args):
762 def _parse(ui, args):
762 options = {}
763 options = {}
763 cmdoptions = {}
764 cmdoptions = {}
764
765
765 try:
766 try:
766 args = fancyopts.fancyopts(args, commands.globalopts, options)
767 args = fancyopts.fancyopts(args, commands.globalopts, options)
767 except getopt.GetoptError as inst:
768 except getopt.GetoptError as inst:
768 raise error.CommandError(None, stringutil.forcebytestr(inst))
769 raise error.CommandError(None, stringutil.forcebytestr(inst))
769
770
770 if args:
771 if args:
771 cmd, args = args[0], args[1:]
772 cmd, args = args[0], args[1:]
772 aliases, entry = cmdutil.findcmd(
773 aliases, entry = cmdutil.findcmd(
773 cmd, commands.table, ui.configbool(b"ui", b"strict")
774 cmd, commands.table, ui.configbool(b"ui", b"strict")
774 )
775 )
775 cmd = aliases[0]
776 cmd = aliases[0]
776 args = aliasargs(entry[0], args)
777 args = aliasargs(entry[0], args)
777 defaults = ui.config(b"defaults", cmd)
778 defaults = ui.config(b"defaults", cmd)
778 if defaults:
779 if defaults:
779 args = (
780 args = (
780 pycompat.maplist(util.expandpath, pycompat.shlexsplit(defaults))
781 pycompat.maplist(util.expandpath, pycompat.shlexsplit(defaults))
781 + args
782 + args
782 )
783 )
783 c = list(entry[1])
784 c = list(entry[1])
784 else:
785 else:
785 cmd = None
786 cmd = None
786 c = []
787 c = []
787
788
788 # combine global options into local
789 # combine global options into local
789 for o in commands.globalopts:
790 for o in commands.globalopts:
790 c.append((o[0], o[1], options[o[1]], o[3]))
791 c.append((o[0], o[1], options[o[1]], o[3]))
791
792
792 try:
793 try:
793 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
794 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
794 except getopt.GetoptError as inst:
795 except getopt.GetoptError as inst:
795 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
796 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
796
797
797 # separate global options back out
798 # separate global options back out
798 for o in commands.globalopts:
799 for o in commands.globalopts:
799 n = o[1]
800 n = o[1]
800 options[n] = cmdoptions[n]
801 options[n] = cmdoptions[n]
801 del cmdoptions[n]
802 del cmdoptions[n]
802
803
803 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
804 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
804
805
805
806
806 def _parseconfig(ui, config):
807 def _parseconfig(ui, config):
807 """parse the --config options from the command line"""
808 """parse the --config options from the command line"""
808 configs = []
809 configs = []
809
810
810 for cfg in config:
811 for cfg in config:
811 try:
812 try:
812 name, value = [cfgelem.strip() for cfgelem in cfg.split(b'=', 1)]
813 name, value = [cfgelem.strip() for cfgelem in cfg.split(b'=', 1)]
813 section, name = name.split(b'.', 1)
814 section, name = name.split(b'.', 1)
814 if not section or not name:
815 if not section or not name:
815 raise IndexError
816 raise IndexError
816 ui.setconfig(section, name, value, b'--config')
817 ui.setconfig(section, name, value, b'--config')
817 configs.append((section, name, value))
818 configs.append((section, name, value))
818 except (IndexError, ValueError):
819 except (IndexError, ValueError):
819 raise error.Abort(
820 raise error.Abort(
820 _(
821 _(
821 b'malformed --config option: %r '
822 b'malformed --config option: %r '
822 b'(use --config section.name=value)'
823 b'(use --config section.name=value)'
823 )
824 )
824 % pycompat.bytestr(cfg)
825 % pycompat.bytestr(cfg)
825 )
826 )
826
827
827 return configs
828 return configs
828
829
829
830
830 def _earlyparseopts(ui, args):
831 def _earlyparseopts(ui, args):
831 options = {}
832 options = {}
832 fancyopts.fancyopts(
833 fancyopts.fancyopts(
833 args,
834 args,
834 commands.globalopts,
835 commands.globalopts,
835 options,
836 options,
836 gnu=not ui.plain(b'strictflags'),
837 gnu=not ui.plain(b'strictflags'),
837 early=True,
838 early=True,
838 optaliases={b'repository': [b'repo']},
839 optaliases={b'repository': [b'repo']},
839 )
840 )
840 return options
841 return options
841
842
842
843
843 def _earlysplitopts(args):
844 def _earlysplitopts(args):
844 """Split args into a list of possible early options and remainder args"""
845 """Split args into a list of possible early options and remainder args"""
845 shortoptions = b'R:'
846 shortoptions = b'R:'
846 # TODO: perhaps 'debugger' should be included
847 # TODO: perhaps 'debugger' should be included
847 longoptions = [b'cwd=', b'repository=', b'repo=', b'config=']
848 longoptions = [b'cwd=', b'repository=', b'repo=', b'config=']
848 return fancyopts.earlygetopt(
849 return fancyopts.earlygetopt(
849 args, shortoptions, longoptions, gnu=True, keepsep=True
850 args, shortoptions, longoptions, gnu=True, keepsep=True
850 )
851 )
851
852
852
853
853 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
854 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
854 # run pre-hook, and abort if it fails
855 # run pre-hook, and abort if it fails
855 hook.hook(
856 hook.hook(
856 lui,
857 lui,
857 repo,
858 repo,
858 b"pre-%s" % cmd,
859 b"pre-%s" % cmd,
859 True,
860 True,
860 args=b" ".join(fullargs),
861 args=b" ".join(fullargs),
861 pats=cmdpats,
862 pats=cmdpats,
862 opts=cmdoptions,
863 opts=cmdoptions,
863 )
864 )
864 try:
865 try:
865 ret = _runcommand(ui, options, cmd, d)
866 ret = _runcommand(ui, options, cmd, d)
866 # run post-hook, passing command result
867 # run post-hook, passing command result
867 hook.hook(
868 hook.hook(
868 lui,
869 lui,
869 repo,
870 repo,
870 b"post-%s" % cmd,
871 b"post-%s" % cmd,
871 False,
872 False,
872 args=b" ".join(fullargs),
873 args=b" ".join(fullargs),
873 result=ret,
874 result=ret,
874 pats=cmdpats,
875 pats=cmdpats,
875 opts=cmdoptions,
876 opts=cmdoptions,
876 )
877 )
877 except Exception:
878 except Exception:
878 # run failure hook and re-raise
879 # run failure hook and re-raise
879 hook.hook(
880 hook.hook(
880 lui,
881 lui,
881 repo,
882 repo,
882 b"fail-%s" % cmd,
883 b"fail-%s" % cmd,
883 False,
884 False,
884 args=b" ".join(fullargs),
885 args=b" ".join(fullargs),
885 pats=cmdpats,
886 pats=cmdpats,
886 opts=cmdoptions,
887 opts=cmdoptions,
887 )
888 )
888 raise
889 raise
889 return ret
890 return ret
890
891
891
892
892 def _getlocal(ui, rpath, wd=None):
893 def _getlocal(ui, rpath, wd=None):
893 """Return (path, local ui object) for the given target path.
894 """Return (path, local ui object) for the given target path.
894
895
895 Takes paths in [cwd]/.hg/hgrc into account."
896 Takes paths in [cwd]/.hg/hgrc into account."
896 """
897 """
897 if wd is None:
898 if wd is None:
898 try:
899 try:
899 wd = encoding.getcwd()
900 wd = encoding.getcwd()
900 except OSError as e:
901 except OSError as e:
901 raise error.Abort(
902 raise error.Abort(
902 _(b"error getting current working directory: %s")
903 _(b"error getting current working directory: %s")
903 % encoding.strtolocal(e.strerror)
904 % encoding.strtolocal(e.strerror)
904 )
905 )
906
905 path = cmdutil.findrepo(wd) or b""
907 path = cmdutil.findrepo(wd) or b""
906 if not path:
908 if not path:
907 lui = ui
909 lui = ui
908 else:
910 else:
909 lui = ui.copy()
911 lui = ui.copy()
910 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
912 if rcutil.use_repo_hgrc():
913 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
911
914
912 if rpath:
915 if rpath:
913 path = lui.expandpath(rpath)
916 path = lui.expandpath(rpath)
914 lui = ui.copy()
917 lui = ui.copy()
915 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
918 if rcutil.use_repo_hgrc():
919 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
916
920
917 return path, lui
921 return path, lui
918
922
919
923
920 def _checkshellalias(lui, ui, args):
924 def _checkshellalias(lui, ui, args):
921 """Return the function to run the shell alias, if it is required"""
925 """Return the function to run the shell alias, if it is required"""
922 options = {}
926 options = {}
923
927
924 try:
928 try:
925 args = fancyopts.fancyopts(args, commands.globalopts, options)
929 args = fancyopts.fancyopts(args, commands.globalopts, options)
926 except getopt.GetoptError:
930 except getopt.GetoptError:
927 return
931 return
928
932
929 if not args:
933 if not args:
930 return
934 return
931
935
932 cmdtable = commands.table
936 cmdtable = commands.table
933
937
934 cmd = args[0]
938 cmd = args[0]
935 try:
939 try:
936 strict = ui.configbool(b"ui", b"strict")
940 strict = ui.configbool(b"ui", b"strict")
937 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
941 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
938 except (error.AmbiguousCommand, error.UnknownCommand):
942 except (error.AmbiguousCommand, error.UnknownCommand):
939 return
943 return
940
944
941 cmd = aliases[0]
945 cmd = aliases[0]
942 fn = entry[0]
946 fn = entry[0]
943
947
944 if cmd and util.safehasattr(fn, b'shell'):
948 if cmd and util.safehasattr(fn, b'shell'):
945 # shell alias shouldn't receive early options which are consumed by hg
949 # shell alias shouldn't receive early options which are consumed by hg
946 _earlyopts, args = _earlysplitopts(args)
950 _earlyopts, args = _earlysplitopts(args)
947 d = lambda: fn(ui, *args[1:])
951 d = lambda: fn(ui, *args[1:])
948 return lambda: runcommand(
952 return lambda: runcommand(
949 lui, None, cmd, args[:1], ui, options, d, [], {}
953 lui, None, cmd, args[:1], ui, options, d, [], {}
950 )
954 )
951
955
952
956
953 def _dispatch(req):
957 def _dispatch(req):
954 args = req.args
958 args = req.args
955 ui = req.ui
959 ui = req.ui
956
960
957 # check for cwd
961 # check for cwd
958 cwd = req.earlyoptions[b'cwd']
962 cwd = req.earlyoptions[b'cwd']
959 if cwd:
963 if cwd:
960 os.chdir(cwd)
964 os.chdir(cwd)
961
965
962 rpath = req.earlyoptions[b'repository']
966 rpath = req.earlyoptions[b'repository']
963 path, lui = _getlocal(ui, rpath)
967 path, lui = _getlocal(ui, rpath)
964
968
965 uis = {ui, lui}
969 uis = {ui, lui}
966
970
967 if req.repo:
971 if req.repo:
968 uis.add(req.repo.ui)
972 uis.add(req.repo.ui)
969
973
970 if (
974 if (
971 req.earlyoptions[b'verbose']
975 req.earlyoptions[b'verbose']
972 or req.earlyoptions[b'debug']
976 or req.earlyoptions[b'debug']
973 or req.earlyoptions[b'quiet']
977 or req.earlyoptions[b'quiet']
974 ):
978 ):
975 for opt in (b'verbose', b'debug', b'quiet'):
979 for opt in (b'verbose', b'debug', b'quiet'):
976 val = pycompat.bytestr(bool(req.earlyoptions[opt]))
980 val = pycompat.bytestr(bool(req.earlyoptions[opt]))
977 for ui_ in uis:
981 for ui_ in uis:
978 ui_.setconfig(b'ui', opt, val, b'--' + opt)
982 ui_.setconfig(b'ui', opt, val, b'--' + opt)
979
983
980 if req.earlyoptions[b'profile']:
984 if req.earlyoptions[b'profile']:
981 for ui_ in uis:
985 for ui_ in uis:
982 ui_.setconfig(b'profiling', b'enabled', b'true', b'--profile')
986 ui_.setconfig(b'profiling', b'enabled', b'true', b'--profile')
983
987
984 profile = lui.configbool(b'profiling', b'enabled')
988 profile = lui.configbool(b'profiling', b'enabled')
985 with profiling.profile(lui, enabled=profile) as profiler:
989 with profiling.profile(lui, enabled=profile) as profiler:
986 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
990 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
987 # reposetup
991 # reposetup
988 extensions.loadall(lui)
992 extensions.loadall(lui)
989 # Propagate any changes to lui.__class__ by extensions
993 # Propagate any changes to lui.__class__ by extensions
990 ui.__class__ = lui.__class__
994 ui.__class__ = lui.__class__
991
995
992 # (uisetup and extsetup are handled in extensions.loadall)
996 # (uisetup and extsetup are handled in extensions.loadall)
993
997
994 # (reposetup is handled in hg.repository)
998 # (reposetup is handled in hg.repository)
995
999
996 addaliases(lui, commands.table)
1000 addaliases(lui, commands.table)
997
1001
998 # All aliases and commands are completely defined, now.
1002 # All aliases and commands are completely defined, now.
999 # Check abbreviation/ambiguity of shell alias.
1003 # Check abbreviation/ambiguity of shell alias.
1000 shellaliasfn = _checkshellalias(lui, ui, args)
1004 shellaliasfn = _checkshellalias(lui, ui, args)
1001 if shellaliasfn:
1005 if shellaliasfn:
1002 # no additional configs will be set, set up the ui instances
1006 # no additional configs will be set, set up the ui instances
1003 for ui_ in uis:
1007 for ui_ in uis:
1004 extensions.populateui(ui_)
1008 extensions.populateui(ui_)
1005 return shellaliasfn()
1009 return shellaliasfn()
1006
1010
1007 # check for fallback encoding
1011 # check for fallback encoding
1008 fallback = lui.config(b'ui', b'fallbackencoding')
1012 fallback = lui.config(b'ui', b'fallbackencoding')
1009 if fallback:
1013 if fallback:
1010 encoding.fallbackencoding = fallback
1014 encoding.fallbackencoding = fallback
1011
1015
1012 fullargs = args
1016 fullargs = args
1013 cmd, func, args, options, cmdoptions = _parse(lui, args)
1017 cmd, func, args, options, cmdoptions = _parse(lui, args)
1014
1018
1015 # store the canonical command name in request object for later access
1019 # store the canonical command name in request object for later access
1016 req.canonical_command = cmd
1020 req.canonical_command = cmd
1017
1021
1018 if options[b"config"] != req.earlyoptions[b"config"]:
1022 if options[b"config"] != req.earlyoptions[b"config"]:
1019 raise error.Abort(_(b"option --config may not be abbreviated!"))
1023 raise error.Abort(_(b"option --config may not be abbreviated!"))
1020 if options[b"cwd"] != req.earlyoptions[b"cwd"]:
1024 if options[b"cwd"] != req.earlyoptions[b"cwd"]:
1021 raise error.Abort(_(b"option --cwd may not be abbreviated!"))
1025 raise error.Abort(_(b"option --cwd may not be abbreviated!"))
1022 if options[b"repository"] != req.earlyoptions[b"repository"]:
1026 if options[b"repository"] != req.earlyoptions[b"repository"]:
1023 raise error.Abort(
1027 raise error.Abort(
1024 _(
1028 _(
1025 b"option -R has to be separated from other options (e.g. not "
1029 b"option -R has to be separated from other options (e.g. not "
1026 b"-qR) and --repository may only be abbreviated as --repo!"
1030 b"-qR) and --repository may only be abbreviated as --repo!"
1027 )
1031 )
1028 )
1032 )
1029 if options[b"debugger"] != req.earlyoptions[b"debugger"]:
1033 if options[b"debugger"] != req.earlyoptions[b"debugger"]:
1030 raise error.Abort(_(b"option --debugger may not be abbreviated!"))
1034 raise error.Abort(_(b"option --debugger may not be abbreviated!"))
1031 # don't validate --profile/--traceback, which can be enabled from now
1035 # don't validate --profile/--traceback, which can be enabled from now
1032
1036
1033 if options[b"encoding"]:
1037 if options[b"encoding"]:
1034 encoding.encoding = options[b"encoding"]
1038 encoding.encoding = options[b"encoding"]
1035 if options[b"encodingmode"]:
1039 if options[b"encodingmode"]:
1036 encoding.encodingmode = options[b"encodingmode"]
1040 encoding.encodingmode = options[b"encodingmode"]
1037 if options[b"time"]:
1041 if options[b"time"]:
1038
1042
1039 def get_times():
1043 def get_times():
1040 t = os.times()
1044 t = os.times()
1041 if t[4] == 0.0:
1045 if t[4] == 0.0:
1042 # Windows leaves this as zero, so use time.perf_counter()
1046 # Windows leaves this as zero, so use time.perf_counter()
1043 t = (t[0], t[1], t[2], t[3], util.timer())
1047 t = (t[0], t[1], t[2], t[3], util.timer())
1044 return t
1048 return t
1045
1049
1046 s = get_times()
1050 s = get_times()
1047
1051
1048 def print_time():
1052 def print_time():
1049 t = get_times()
1053 t = get_times()
1050 ui.warn(
1054 ui.warn(
1051 _(b"time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n")
1055 _(b"time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n")
1052 % (
1056 % (
1053 t[4] - s[4],
1057 t[4] - s[4],
1054 t[0] - s[0],
1058 t[0] - s[0],
1055 t[2] - s[2],
1059 t[2] - s[2],
1056 t[1] - s[1],
1060 t[1] - s[1],
1057 t[3] - s[3],
1061 t[3] - s[3],
1058 )
1062 )
1059 )
1063 )
1060
1064
1061 ui.atexit(print_time)
1065 ui.atexit(print_time)
1062 if options[b"profile"]:
1066 if options[b"profile"]:
1063 profiler.start()
1067 profiler.start()
1064
1068
1065 # if abbreviated version of this were used, take them in account, now
1069 # if abbreviated version of this were used, take them in account, now
1066 if options[b'verbose'] or options[b'debug'] or options[b'quiet']:
1070 if options[b'verbose'] or options[b'debug'] or options[b'quiet']:
1067 for opt in (b'verbose', b'debug', b'quiet'):
1071 for opt in (b'verbose', b'debug', b'quiet'):
1068 if options[opt] == req.earlyoptions[opt]:
1072 if options[opt] == req.earlyoptions[opt]:
1069 continue
1073 continue
1070 val = pycompat.bytestr(bool(options[opt]))
1074 val = pycompat.bytestr(bool(options[opt]))
1071 for ui_ in uis:
1075 for ui_ in uis:
1072 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1076 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1073
1077
1074 if options[b'traceback']:
1078 if options[b'traceback']:
1075 for ui_ in uis:
1079 for ui_ in uis:
1076 ui_.setconfig(b'ui', b'traceback', b'on', b'--traceback')
1080 ui_.setconfig(b'ui', b'traceback', b'on', b'--traceback')
1077
1081
1078 if options[b'noninteractive']:
1082 if options[b'noninteractive']:
1079 for ui_ in uis:
1083 for ui_ in uis:
1080 ui_.setconfig(b'ui', b'interactive', b'off', b'-y')
1084 ui_.setconfig(b'ui', b'interactive', b'off', b'-y')
1081
1085
1082 if cmdoptions.get(b'insecure', False):
1086 if cmdoptions.get(b'insecure', False):
1083 for ui_ in uis:
1087 for ui_ in uis:
1084 ui_.insecureconnections = True
1088 ui_.insecureconnections = True
1085
1089
1086 # setup color handling before pager, because setting up pager
1090 # setup color handling before pager, because setting up pager
1087 # might cause incorrect console information
1091 # might cause incorrect console information
1088 coloropt = options[b'color']
1092 coloropt = options[b'color']
1089 for ui_ in uis:
1093 for ui_ in uis:
1090 if coloropt:
1094 if coloropt:
1091 ui_.setconfig(b'ui', b'color', coloropt, b'--color')
1095 ui_.setconfig(b'ui', b'color', coloropt, b'--color')
1092 color.setup(ui_)
1096 color.setup(ui_)
1093
1097
1094 if stringutil.parsebool(options[b'pager']):
1098 if stringutil.parsebool(options[b'pager']):
1095 # ui.pager() expects 'internal-always-' prefix in this case
1099 # ui.pager() expects 'internal-always-' prefix in this case
1096 ui.pager(b'internal-always-' + cmd)
1100 ui.pager(b'internal-always-' + cmd)
1097 elif options[b'pager'] != b'auto':
1101 elif options[b'pager'] != b'auto':
1098 for ui_ in uis:
1102 for ui_ in uis:
1099 ui_.disablepager()
1103 ui_.disablepager()
1100
1104
1101 # configs are fully loaded, set up the ui instances
1105 # configs are fully loaded, set up the ui instances
1102 for ui_ in uis:
1106 for ui_ in uis:
1103 extensions.populateui(ui_)
1107 extensions.populateui(ui_)
1104
1108
1105 if options[b'version']:
1109 if options[b'version']:
1106 return commands.version_(ui)
1110 return commands.version_(ui)
1107 if options[b'help']:
1111 if options[b'help']:
1108 return commands.help_(ui, cmd, command=cmd is not None)
1112 return commands.help_(ui, cmd, command=cmd is not None)
1109 elif not cmd:
1113 elif not cmd:
1110 return commands.help_(ui, b'shortlist')
1114 return commands.help_(ui, b'shortlist')
1111
1115
1112 repo = None
1116 repo = None
1113 cmdpats = args[:]
1117 cmdpats = args[:]
1114 assert func is not None # help out pytype
1118 assert func is not None # help out pytype
1115 if not func.norepo:
1119 if not func.norepo:
1116 # use the repo from the request only if we don't have -R
1120 # use the repo from the request only if we don't have -R
1117 if not rpath and not cwd:
1121 if not rpath and not cwd:
1118 repo = req.repo
1122 repo = req.repo
1119
1123
1120 if repo:
1124 if repo:
1121 # set the descriptors of the repo ui to those of ui
1125 # set the descriptors of the repo ui to those of ui
1122 repo.ui.fin = ui.fin
1126 repo.ui.fin = ui.fin
1123 repo.ui.fout = ui.fout
1127 repo.ui.fout = ui.fout
1124 repo.ui.ferr = ui.ferr
1128 repo.ui.ferr = ui.ferr
1125 repo.ui.fmsg = ui.fmsg
1129 repo.ui.fmsg = ui.fmsg
1126 else:
1130 else:
1127 try:
1131 try:
1128 repo = hg.repository(
1132 repo = hg.repository(
1129 ui,
1133 ui,
1130 path=path,
1134 path=path,
1131 presetupfuncs=req.prereposetups,
1135 presetupfuncs=req.prereposetups,
1132 intents=func.intents,
1136 intents=func.intents,
1133 )
1137 )
1134 if not repo.local():
1138 if not repo.local():
1135 raise error.Abort(
1139 raise error.Abort(
1136 _(b"repository '%s' is not local") % path
1140 _(b"repository '%s' is not local") % path
1137 )
1141 )
1138 repo.ui.setconfig(
1142 repo.ui.setconfig(
1139 b"bundle", b"mainreporoot", repo.root, b'repo'
1143 b"bundle", b"mainreporoot", repo.root, b'repo'
1140 )
1144 )
1141 except error.RequirementError:
1145 except error.RequirementError:
1142 raise
1146 raise
1143 except error.RepoError:
1147 except error.RepoError:
1144 if rpath: # invalid -R path
1148 if rpath: # invalid -R path
1145 raise
1149 raise
1146 if not func.optionalrepo:
1150 if not func.optionalrepo:
1147 if func.inferrepo and args and not path:
1151 if func.inferrepo and args and not path:
1148 # try to infer -R from command args
1152 # try to infer -R from command args
1149 repos = pycompat.maplist(cmdutil.findrepo, args)
1153 repos = pycompat.maplist(cmdutil.findrepo, args)
1150 guess = repos[0]
1154 guess = repos[0]
1151 if guess and repos.count(guess) == len(repos):
1155 if guess and repos.count(guess) == len(repos):
1152 req.args = [b'--repository', guess] + fullargs
1156 req.args = [b'--repository', guess] + fullargs
1153 req.earlyoptions[b'repository'] = guess
1157 req.earlyoptions[b'repository'] = guess
1154 return _dispatch(req)
1158 return _dispatch(req)
1155 if not path:
1159 if not path:
1156 raise error.RepoError(
1160 raise error.RepoError(
1157 _(
1161 _(
1158 b"no repository found in"
1162 b"no repository found in"
1159 b" '%s' (.hg not found)"
1163 b" '%s' (.hg not found)"
1160 )
1164 )
1161 % encoding.getcwd()
1165 % encoding.getcwd()
1162 )
1166 )
1163 raise
1167 raise
1164 if repo:
1168 if repo:
1165 ui = repo.ui
1169 ui = repo.ui
1166 if options[b'hidden']:
1170 if options[b'hidden']:
1167 repo = repo.unfiltered()
1171 repo = repo.unfiltered()
1168 args.insert(0, repo)
1172 args.insert(0, repo)
1169 elif rpath:
1173 elif rpath:
1170 ui.warn(_(b"warning: --repository ignored\n"))
1174 ui.warn(_(b"warning: --repository ignored\n"))
1171
1175
1172 msg = _formatargs(fullargs)
1176 msg = _formatargs(fullargs)
1173 ui.log(b"command", b'%s\n', msg)
1177 ui.log(b"command", b'%s\n', msg)
1174 strcmdopt = pycompat.strkwargs(cmdoptions)
1178 strcmdopt = pycompat.strkwargs(cmdoptions)
1175 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1179 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1176 try:
1180 try:
1177 return runcommand(
1181 return runcommand(
1178 lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions
1182 lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions
1179 )
1183 )
1180 finally:
1184 finally:
1181 if repo and repo != req.repo:
1185 if repo and repo != req.repo:
1182 repo.close()
1186 repo.close()
1183
1187
1184
1188
1185 def _runcommand(ui, options, cmd, cmdfunc):
1189 def _runcommand(ui, options, cmd, cmdfunc):
1186 """Run a command function, possibly with profiling enabled."""
1190 """Run a command function, possibly with profiling enabled."""
1187 try:
1191 try:
1188 with tracing.log("Running %s command" % cmd):
1192 with tracing.log("Running %s command" % cmd):
1189 return cmdfunc()
1193 return cmdfunc()
1190 except error.SignatureError:
1194 except error.SignatureError:
1191 raise error.CommandError(cmd, _(b'invalid arguments'))
1195 raise error.CommandError(cmd, _(b'invalid arguments'))
1192
1196
1193
1197
1194 def _exceptionwarning(ui):
1198 def _exceptionwarning(ui):
1195 """Produce a warning message for the current active exception"""
1199 """Produce a warning message for the current active exception"""
1196
1200
1197 # For compatibility checking, we discard the portion of the hg
1201 # For compatibility checking, we discard the portion of the hg
1198 # version after the + on the assumption that if a "normal
1202 # version after the + on the assumption that if a "normal
1199 # user" is running a build with a + in it the packager
1203 # user" is running a build with a + in it the packager
1200 # probably built from fairly close to a tag and anyone with a
1204 # probably built from fairly close to a tag and anyone with a
1201 # 'make local' copy of hg (where the version number can be out
1205 # 'make local' copy of hg (where the version number can be out
1202 # of date) will be clueful enough to notice the implausible
1206 # of date) will be clueful enough to notice the implausible
1203 # version number and try updating.
1207 # version number and try updating.
1204 ct = util.versiontuple(n=2)
1208 ct = util.versiontuple(n=2)
1205 worst = None, ct, b''
1209 worst = None, ct, b''
1206 if ui.config(b'ui', b'supportcontact') is None:
1210 if ui.config(b'ui', b'supportcontact') is None:
1207 for name, mod in extensions.extensions():
1211 for name, mod in extensions.extensions():
1208 # 'testedwith' should be bytes, but not all extensions are ported
1212 # 'testedwith' should be bytes, but not all extensions are ported
1209 # to py3 and we don't want UnicodeException because of that.
1213 # to py3 and we don't want UnicodeException because of that.
1210 testedwith = stringutil.forcebytestr(
1214 testedwith = stringutil.forcebytestr(
1211 getattr(mod, 'testedwith', b'')
1215 getattr(mod, 'testedwith', b'')
1212 )
1216 )
1213 report = getattr(mod, 'buglink', _(b'the extension author.'))
1217 report = getattr(mod, 'buglink', _(b'the extension author.'))
1214 if not testedwith.strip():
1218 if not testedwith.strip():
1215 # We found an untested extension. It's likely the culprit.
1219 # We found an untested extension. It's likely the culprit.
1216 worst = name, b'unknown', report
1220 worst = name, b'unknown', report
1217 break
1221 break
1218
1222
1219 # Never blame on extensions bundled with Mercurial.
1223 # Never blame on extensions bundled with Mercurial.
1220 if extensions.ismoduleinternal(mod):
1224 if extensions.ismoduleinternal(mod):
1221 continue
1225 continue
1222
1226
1223 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1227 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1224 if ct in tested:
1228 if ct in tested:
1225 continue
1229 continue
1226
1230
1227 lower = [t for t in tested if t < ct]
1231 lower = [t for t in tested if t < ct]
1228 nearest = max(lower or tested)
1232 nearest = max(lower or tested)
1229 if worst[0] is None or nearest < worst[1]:
1233 if worst[0] is None or nearest < worst[1]:
1230 worst = name, nearest, report
1234 worst = name, nearest, report
1231 if worst[0] is not None:
1235 if worst[0] is not None:
1232 name, testedwith, report = worst
1236 name, testedwith, report = worst
1233 if not isinstance(testedwith, (bytes, str)):
1237 if not isinstance(testedwith, (bytes, str)):
1234 testedwith = b'.'.join(
1238 testedwith = b'.'.join(
1235 [stringutil.forcebytestr(c) for c in testedwith]
1239 [stringutil.forcebytestr(c) for c in testedwith]
1236 )
1240 )
1237 warning = _(
1241 warning = _(
1238 b'** Unknown exception encountered with '
1242 b'** Unknown exception encountered with '
1239 b'possibly-broken third-party extension %s\n'
1243 b'possibly-broken third-party extension %s\n'
1240 b'** which supports versions %s of Mercurial.\n'
1244 b'** which supports versions %s of Mercurial.\n'
1241 b'** Please disable %s and try your action again.\n'
1245 b'** Please disable %s and try your action again.\n'
1242 b'** If that fixes the bug please report it to %s\n'
1246 b'** If that fixes the bug please report it to %s\n'
1243 ) % (name, testedwith, name, stringutil.forcebytestr(report))
1247 ) % (name, testedwith, name, stringutil.forcebytestr(report))
1244 else:
1248 else:
1245 bugtracker = ui.config(b'ui', b'supportcontact')
1249 bugtracker = ui.config(b'ui', b'supportcontact')
1246 if bugtracker is None:
1250 if bugtracker is None:
1247 bugtracker = _(b"https://mercurial-scm.org/wiki/BugTracker")
1251 bugtracker = _(b"https://mercurial-scm.org/wiki/BugTracker")
1248 warning = (
1252 warning = (
1249 _(
1253 _(
1250 b"** unknown exception encountered, "
1254 b"** unknown exception encountered, "
1251 b"please report by visiting\n** "
1255 b"please report by visiting\n** "
1252 )
1256 )
1253 + bugtracker
1257 + bugtracker
1254 + b'\n'
1258 + b'\n'
1255 )
1259 )
1256 sysversion = pycompat.sysbytes(sys.version).replace(b'\n', b'')
1260 sysversion = pycompat.sysbytes(sys.version).replace(b'\n', b'')
1257 warning += (
1261 warning += (
1258 (_(b"** Python %s\n") % sysversion)
1262 (_(b"** Python %s\n") % sysversion)
1259 + (_(b"** Mercurial Distributed SCM (version %s)\n") % util.version())
1263 + (_(b"** Mercurial Distributed SCM (version %s)\n") % util.version())
1260 + (
1264 + (
1261 _(b"** Extensions loaded: %s\n")
1265 _(b"** Extensions loaded: %s\n")
1262 % b", ".join([x[0] for x in extensions.extensions()])
1266 % b", ".join([x[0] for x in extensions.extensions()])
1263 )
1267 )
1264 )
1268 )
1265 return warning
1269 return warning
1266
1270
1267
1271
1268 def handlecommandexception(ui):
1272 def handlecommandexception(ui):
1269 """Produce a warning message for broken commands
1273 """Produce a warning message for broken commands
1270
1274
1271 Called when handling an exception; the exception is reraised if
1275 Called when handling an exception; the exception is reraised if
1272 this function returns False, ignored otherwise.
1276 this function returns False, ignored otherwise.
1273 """
1277 """
1274 warning = _exceptionwarning(ui)
1278 warning = _exceptionwarning(ui)
1275 ui.log(
1279 ui.log(
1276 b"commandexception",
1280 b"commandexception",
1277 b"%s\n%s\n",
1281 b"%s\n%s\n",
1278 warning,
1282 warning,
1279 pycompat.sysbytes(traceback.format_exc()),
1283 pycompat.sysbytes(traceback.format_exc()),
1280 )
1284 )
1281 ui.warn(warning)
1285 ui.warn(warning)
1282 return False # re-raise the exception
1286 return False # re-raise the exception
@@ -1,273 +1,283 b''
1 Use hgrc within $TESTTMP
1 Use hgrc within $TESTTMP
2
2
3 $ HGRCPATH=`pwd`/hgrc
3 $ HGRCPATH=`pwd`/hgrc
4 $ export HGRCPATH
4 $ export HGRCPATH
5
5
6 hide outer repo
6 hide outer repo
7 $ hg init
7 $ hg init
8
8
9 Use an alternate var for scribbling on hgrc to keep check-code from
9 Use an alternate var for scribbling on hgrc to keep check-code from
10 complaining about the important settings we may be overwriting:
10 complaining about the important settings we may be overwriting:
11
11
12 $ HGRC=`pwd`/hgrc
12 $ HGRC=`pwd`/hgrc
13 $ export HGRC
13 $ export HGRC
14
14
15 Basic syntax error
15 Basic syntax error
16
16
17 $ echo "invalid" > $HGRC
17 $ echo "invalid" > $HGRC
18 $ hg version
18 $ hg version
19 hg: parse error at $TESTTMP/hgrc:1: invalid
19 hg: parse error at $TESTTMP/hgrc:1: invalid
20 [255]
20 [255]
21 $ echo "" > $HGRC
21 $ echo "" > $HGRC
22
22
23 Issue1199: Can't use '%' in hgrc (eg url encoded username)
23 Issue1199: Can't use '%' in hgrc (eg url encoded username)
24
24
25 $ hg init "foo%bar"
25 $ hg init "foo%bar"
26 $ hg clone "foo%bar" foobar
26 $ hg clone "foo%bar" foobar
27 updating to branch default
27 updating to branch default
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 $ cd foobar
29 $ cd foobar
30 $ cat .hg/hgrc
30 $ cat .hg/hgrc
31 # example repository config (see 'hg help config' for more info)
31 # example repository config (see 'hg help config' for more info)
32 [paths]
32 [paths]
33 default = $TESTTMP/foo%bar
33 default = $TESTTMP/foo%bar
34
34
35 # path aliases to other clones of this repo in URLs or filesystem paths
35 # path aliases to other clones of this repo in URLs or filesystem paths
36 # (see 'hg help config.paths' for more info)
36 # (see 'hg help config.paths' for more info)
37 #
37 #
38 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
38 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
40 # my-clone = /home/jdoe/jdoes-clone
40 # my-clone = /home/jdoe/jdoes-clone
41
41
42 [ui]
42 [ui]
43 # name and email (local to this repository, optional), e.g.
43 # name and email (local to this repository, optional), e.g.
44 # username = Jane Doe <jdoe@example.com>
44 # username = Jane Doe <jdoe@example.com>
45 $ hg paths
45 $ hg paths
46 default = $TESTTMP/foo%bar
46 default = $TESTTMP/foo%bar
47 $ hg showconfig
47 $ hg showconfig
48 bundle.mainreporoot=$TESTTMP/foobar
48 bundle.mainreporoot=$TESTTMP/foobar
49 paths.default=$TESTTMP/foo%bar
49 paths.default=$TESTTMP/foo%bar
50 $ cd ..
50 $ cd ..
51
51
52 Check %include
52 Check %include
53
53
54 $ echo '[section]' > $TESTTMP/included
54 $ echo '[section]' > $TESTTMP/included
55 $ echo 'option = value' >> $TESTTMP/included
55 $ echo 'option = value' >> $TESTTMP/included
56 $ echo '%include $TESTTMP/included' >> $HGRC
56 $ echo '%include $TESTTMP/included' >> $HGRC
57 $ hg showconfig section
57 $ hg showconfig section
58 section.option=value
58 section.option=value
59 #if no-windows
59 #if no-windows
60 $ chmod u-r $TESTTMP/included
60 $ chmod u-r $TESTTMP/included
61 $ hg showconfig section
61 $ hg showconfig section
62 hg: parse error at $TESTTMP/hgrc:2: cannot include $TESTTMP/included (Permission denied)
62 hg: parse error at $TESTTMP/hgrc:2: cannot include $TESTTMP/included (Permission denied)
63 [255]
63 [255]
64 #endif
64 #endif
65
65
66 issue1829: wrong indentation
66 issue1829: wrong indentation
67
67
68 $ echo '[foo]' > $HGRC
68 $ echo '[foo]' > $HGRC
69 $ echo ' x = y' >> $HGRC
69 $ echo ' x = y' >> $HGRC
70 $ hg version
70 $ hg version
71 hg: parse error at $TESTTMP/hgrc:2: x = y
71 hg: parse error at $TESTTMP/hgrc:2: x = y
72 unexpected leading whitespace
72 unexpected leading whitespace
73 [255]
73 [255]
74
74
75 $ "$PYTHON" -c "from __future__ import print_function; print('[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n')" \
75 $ "$PYTHON" -c "from __future__ import print_function; print('[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n')" \
76 > > $HGRC
76 > > $HGRC
77 $ hg showconfig foo
77 $ hg showconfig foo
78 foo.bar=a\nb\nc\nde\nfg
78 foo.bar=a\nb\nc\nde\nfg
79 foo.baz=bif cb
79 foo.baz=bif cb
80
80
81 $ FAKEPATH=/path/to/nowhere
81 $ FAKEPATH=/path/to/nowhere
82 $ export FAKEPATH
82 $ export FAKEPATH
83 $ echo '%include $FAKEPATH/no-such-file' > $HGRC
83 $ echo '%include $FAKEPATH/no-such-file' > $HGRC
84 $ hg version
84 $ hg version
85 Mercurial Distributed SCM (version *) (glob)
85 Mercurial Distributed SCM (version *) (glob)
86 (see https://mercurial-scm.org for more information)
86 (see https://mercurial-scm.org for more information)
87
87
88 Copyright (C) 2005-* Matt Mackall and others (glob)
88 Copyright (C) 2005-* Matt Mackall and others (glob)
89 This is free software; see the source for copying conditions. There is NO
89 This is free software; see the source for copying conditions. There is NO
90 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
90 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
91 $ unset FAKEPATH
91 $ unset FAKEPATH
92
92
93 make sure global options given on the cmdline take precedence
93 make sure global options given on the cmdline take precedence
94
94
95 $ hg showconfig --config ui.verbose=True --quiet
95 $ hg showconfig --config ui.verbose=True --quiet
96 bundle.mainreporoot=$TESTTMP
96 bundle.mainreporoot=$TESTTMP
97 ui.verbose=False
97 ui.verbose=False
98 ui.debug=False
98 ui.debug=False
99 ui.quiet=True
99 ui.quiet=True
100
100
101 $ touch foobar/untracked
101 $ touch foobar/untracked
102 $ cat >> foobar/.hg/hgrc <<EOF
102 $ cat >> foobar/.hg/hgrc <<EOF
103 > [ui]
103 > [ui]
104 > verbose=True
104 > verbose=True
105 > EOF
105 > EOF
106 $ hg -R foobar st -q
106 $ hg -R foobar st -q
107
107
108 username expansion
108 username expansion
109
109
110 $ olduser=$HGUSER
110 $ olduser=$HGUSER
111 $ unset HGUSER
111 $ unset HGUSER
112
112
113 $ FAKEUSER='John Doe'
113 $ FAKEUSER='John Doe'
114 $ export FAKEUSER
114 $ export FAKEUSER
115 $ echo '[ui]' > $HGRC
115 $ echo '[ui]' > $HGRC
116 $ echo 'username = $FAKEUSER' >> $HGRC
116 $ echo 'username = $FAKEUSER' >> $HGRC
117
117
118 $ hg init usertest
118 $ hg init usertest
119 $ cd usertest
119 $ cd usertest
120 $ touch bar
120 $ touch bar
121 $ hg commit --addremove --quiet -m "added bar"
121 $ hg commit --addremove --quiet -m "added bar"
122 $ hg log --template "{author}\n"
122 $ hg log --template "{author}\n"
123 John Doe
123 John Doe
124 $ cd ..
124 $ cd ..
125
125
126 $ hg showconfig
126 $ hg showconfig
127 bundle.mainreporoot=$TESTTMP
127 bundle.mainreporoot=$TESTTMP
128 ui.username=$FAKEUSER
128 ui.username=$FAKEUSER
129
129
130 $ unset FAKEUSER
130 $ unset FAKEUSER
131 $ HGUSER=$olduser
131 $ HGUSER=$olduser
132 $ export HGUSER
132 $ export HGUSER
133
133
134 showconfig with multiple arguments
134 showconfig with multiple arguments
135
135
136 $ echo "[alias]" > $HGRC
136 $ echo "[alias]" > $HGRC
137 $ echo "log = log -g" >> $HGRC
137 $ echo "log = log -g" >> $HGRC
138 $ echo "[defaults]" >> $HGRC
138 $ echo "[defaults]" >> $HGRC
139 $ echo "identify = -n" >> $HGRC
139 $ echo "identify = -n" >> $HGRC
140 $ hg showconfig alias defaults
140 $ hg showconfig alias defaults
141 alias.log=log -g
141 alias.log=log -g
142 defaults.identify=-n
142 defaults.identify=-n
143 $ hg showconfig alias alias
143 $ hg showconfig alias alias
144 alias.log=log -g
144 alias.log=log -g
145 $ hg showconfig alias.log alias.log
145 $ hg showconfig alias.log alias.log
146 alias.log=log -g
146 alias.log=log -g
147 $ hg showconfig alias defaults.identify
147 $ hg showconfig alias defaults.identify
148 alias.log=log -g
148 alias.log=log -g
149 defaults.identify=-n
149 defaults.identify=-n
150 $ hg showconfig alias.log defaults.identify
150 $ hg showconfig alias.log defaults.identify
151 alias.log=log -g
151 alias.log=log -g
152 defaults.identify=-n
152 defaults.identify=-n
153
153
154 HGPLAIN
154 HGPLAIN
155
155
156 $ echo "[ui]" > $HGRC
156 $ echo "[ui]" > $HGRC
157 $ echo "debug=true" >> $HGRC
157 $ echo "debug=true" >> $HGRC
158 $ echo "fallbackencoding=ASCII" >> $HGRC
158 $ echo "fallbackencoding=ASCII" >> $HGRC
159 $ echo "quiet=true" >> $HGRC
159 $ echo "quiet=true" >> $HGRC
160 $ echo "slash=true" >> $HGRC
160 $ echo "slash=true" >> $HGRC
161 $ echo "traceback=true" >> $HGRC
161 $ echo "traceback=true" >> $HGRC
162 $ echo "verbose=true" >> $HGRC
162 $ echo "verbose=true" >> $HGRC
163 $ echo "style=~/.hgstyle" >> $HGRC
163 $ echo "style=~/.hgstyle" >> $HGRC
164 $ echo "logtemplate={node}" >> $HGRC
164 $ echo "logtemplate={node}" >> $HGRC
165 $ echo "[defaults]" >> $HGRC
165 $ echo "[defaults]" >> $HGRC
166 $ echo "identify=-n" >> $HGRC
166 $ echo "identify=-n" >> $HGRC
167 $ echo "[alias]" >> $HGRC
167 $ echo "[alias]" >> $HGRC
168 $ echo "log=log -g" >> $HGRC
168 $ echo "log=log -g" >> $HGRC
169
169
170 customized hgrc
170 customized hgrc
171
171
172 $ hg showconfig
172 $ hg showconfig
173 read config from: $TESTTMP/hgrc
173 read config from: $TESTTMP/hgrc
174 $TESTTMP/hgrc:13: alias.log=log -g
174 $TESTTMP/hgrc:13: alias.log=log -g
175 repo: bundle.mainreporoot=$TESTTMP
175 repo: bundle.mainreporoot=$TESTTMP
176 $TESTTMP/hgrc:11: defaults.identify=-n
176 $TESTTMP/hgrc:11: defaults.identify=-n
177 $TESTTMP/hgrc:2: ui.debug=true
177 $TESTTMP/hgrc:2: ui.debug=true
178 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
178 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
179 $TESTTMP/hgrc:4: ui.quiet=true
179 $TESTTMP/hgrc:4: ui.quiet=true
180 $TESTTMP/hgrc:5: ui.slash=true
180 $TESTTMP/hgrc:5: ui.slash=true
181 $TESTTMP/hgrc:6: ui.traceback=true
181 $TESTTMP/hgrc:6: ui.traceback=true
182 $TESTTMP/hgrc:7: ui.verbose=true
182 $TESTTMP/hgrc:7: ui.verbose=true
183 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
183 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
184 $TESTTMP/hgrc:9: ui.logtemplate={node}
184 $TESTTMP/hgrc:9: ui.logtemplate={node}
185
185
186 plain hgrc
186 plain hgrc
187
187
188 $ HGPLAIN=; export HGPLAIN
188 $ HGPLAIN=; export HGPLAIN
189 $ hg showconfig --config ui.traceback=True --debug
189 $ hg showconfig --config ui.traceback=True --debug
190 read config from: $TESTTMP/hgrc
190 read config from: $TESTTMP/hgrc
191 repo: bundle.mainreporoot=$TESTTMP
191 repo: bundle.mainreporoot=$TESTTMP
192 --config: ui.traceback=True
192 --config: ui.traceback=True
193 --verbose: ui.verbose=False
193 --verbose: ui.verbose=False
194 --debug: ui.debug=True
194 --debug: ui.debug=True
195 --quiet: ui.quiet=False
195 --quiet: ui.quiet=False
196
196
197 with environment variables
197 with environment variables
198
198
199 $ PAGER=p1 EDITOR=e1 VISUAL=e2 hg showconfig --debug
199 $ PAGER=p1 EDITOR=e1 VISUAL=e2 hg showconfig --debug
200 read config from: $TESTTMP/hgrc
200 read config from: $TESTTMP/hgrc
201 repo: bundle.mainreporoot=$TESTTMP
201 repo: bundle.mainreporoot=$TESTTMP
202 $PAGER: pager.pager=p1
202 $PAGER: pager.pager=p1
203 $VISUAL: ui.editor=e2
203 $VISUAL: ui.editor=e2
204 --verbose: ui.verbose=False
204 --verbose: ui.verbose=False
205 --debug: ui.debug=True
205 --debug: ui.debug=True
206 --quiet: ui.quiet=False
206 --quiet: ui.quiet=False
207
207
208 plain mode with exceptions
208 plain mode with exceptions
209
209
210 $ cat > plain.py <<EOF
210 $ cat > plain.py <<EOF
211 > from mercurial import commands, extensions
211 > from mercurial import commands, extensions
212 > def _config(orig, ui, repo, *values, **opts):
212 > def _config(orig, ui, repo, *values, **opts):
213 > ui.write(b'plain: %r\n' % ui.plain())
213 > ui.write(b'plain: %r\n' % ui.plain())
214 > return orig(ui, repo, *values, **opts)
214 > return orig(ui, repo, *values, **opts)
215 > def uisetup(ui):
215 > def uisetup(ui):
216 > extensions.wrapcommand(commands.table, b'config', _config)
216 > extensions.wrapcommand(commands.table, b'config', _config)
217 > EOF
217 > EOF
218 $ echo "[extensions]" >> $HGRC
218 $ echo "[extensions]" >> $HGRC
219 $ echo "plain=./plain.py" >> $HGRC
219 $ echo "plain=./plain.py" >> $HGRC
220 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
220 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
221 $ hg showconfig --config ui.traceback=True --debug
221 $ hg showconfig --config ui.traceback=True --debug
222 plain: True
222 plain: True
223 read config from: $TESTTMP/hgrc
223 read config from: $TESTTMP/hgrc
224 repo: bundle.mainreporoot=$TESTTMP
224 repo: bundle.mainreporoot=$TESTTMP
225 $TESTTMP/hgrc:15: extensions.plain=./plain.py
225 $TESTTMP/hgrc:15: extensions.plain=./plain.py
226 --config: ui.traceback=True
226 --config: ui.traceback=True
227 --verbose: ui.verbose=False
227 --verbose: ui.verbose=False
228 --debug: ui.debug=True
228 --debug: ui.debug=True
229 --quiet: ui.quiet=False
229 --quiet: ui.quiet=False
230 $ unset HGPLAIN
230 $ unset HGPLAIN
231 $ hg showconfig --config ui.traceback=True --debug
231 $ hg showconfig --config ui.traceback=True --debug
232 plain: True
232 plain: True
233 read config from: $TESTTMP/hgrc
233 read config from: $TESTTMP/hgrc
234 repo: bundle.mainreporoot=$TESTTMP
234 repo: bundle.mainreporoot=$TESTTMP
235 $TESTTMP/hgrc:15: extensions.plain=./plain.py
235 $TESTTMP/hgrc:15: extensions.plain=./plain.py
236 --config: ui.traceback=True
236 --config: ui.traceback=True
237 --verbose: ui.verbose=False
237 --verbose: ui.verbose=False
238 --debug: ui.debug=True
238 --debug: ui.debug=True
239 --quiet: ui.quiet=False
239 --quiet: ui.quiet=False
240 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
240 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
241 $ hg showconfig --config ui.traceback=True --debug
241 $ hg showconfig --config ui.traceback=True --debug
242 plain: True
242 plain: True
243 read config from: $TESTTMP/hgrc
243 read config from: $TESTTMP/hgrc
244 repo: bundle.mainreporoot=$TESTTMP
244 repo: bundle.mainreporoot=$TESTTMP
245 $TESTTMP/hgrc:15: extensions.plain=./plain.py
245 $TESTTMP/hgrc:15: extensions.plain=./plain.py
246 --config: ui.traceback=True
246 --config: ui.traceback=True
247 --verbose: ui.verbose=False
247 --verbose: ui.verbose=False
248 --debug: ui.debug=True
248 --debug: ui.debug=True
249 --quiet: ui.quiet=False
249 --quiet: ui.quiet=False
250
250
251 source of paths is not mangled
251 source of paths is not mangled
252
252
253 $ cat >> $HGRCPATH <<EOF
253 $ cat >> $HGRCPATH <<EOF
254 > [paths]
254 > [paths]
255 > foo = bar
255 > foo = bar
256 > EOF
256 > EOF
257 $ hg showconfig --debug paths
257 $ hg showconfig --debug paths
258 plain: True
258 plain: True
259 read config from: $TESTTMP/hgrc
259 read config from: $TESTTMP/hgrc
260 $TESTTMP/hgrc:17: paths.foo=$TESTTMP/bar
260 $TESTTMP/hgrc:17: paths.foo=$TESTTMP/bar
261
261
262 Test we can skip the user configuration
262 Test we can skip the user configuration
263
263
264 $ cat >> .hg/hgrc <<EOF
264 $ cat >> .hg/hgrc <<EOF
265 > [paths]
265 > [paths]
266 > elephant = babar
266 > elephant = babar
267 > EOF
267 > EOF
268 $ hg path
268 $ hg path
269 elephant = $TESTTMP/babar
269 elephant = $TESTTMP/babar
270 foo = $TESTTMP/bar
270 foo = $TESTTMP/bar
271 $ HGRCSKIPREPO=1 hg path
271 $ HGRCSKIPREPO=1 hg path
272 foo = $TESTTMP/bar
272 foo = $TESTTMP/bar
273
273
274 $ cat >> .hg/hgrc <<EOF
275 > [broken
276 > EOF
277
278 $ hg path
279 hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
280 [255]
281 $ HGRCSKIPREPO=1 hg path
282 foo = $TESTTMP/bar
283
General Comments 0
You need to be logged in to leave comments. Login now