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