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