##// END OF EJS Templates
backout: backed out changeset 6edc8800dbc3...
Raphaël Gomès -
r49071:8c34edb1 default
parent child Browse files
Show More
@@ -1,1407 +1,1404 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import errno
10 import errno
11 import getopt
11 import getopt
12 import io
12 import io
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import signal
16 import signal
17 import sys
17 import sys
18 import traceback
18 import traceback
19
19
20
20
21 from .i18n import _
21 from .i18n import _
22 from .pycompat import getattr
22 from .pycompat import getattr
23
23
24 from hgdemandimport import tracing
24 from hgdemandimport import tracing
25
25
26 from . import (
26 from . import (
27 cmdutil,
27 cmdutil,
28 color,
28 color,
29 commands,
29 commands,
30 demandimport,
30 demandimport,
31 encoding,
31 encoding,
32 error,
32 error,
33 extensions,
33 extensions,
34 fancyopts,
34 fancyopts,
35 help,
35 help,
36 hg,
36 hg,
37 hook,
37 hook,
38 localrepo,
38 localrepo,
39 profiling,
39 profiling,
40 pycompat,
40 pycompat,
41 rcutil,
41 rcutil,
42 registrar,
42 registrar,
43 requirements as requirementsmod,
43 requirements as requirementsmod,
44 scmutil,
44 scmutil,
45 ui as uimod,
45 ui as uimod,
46 util,
46 util,
47 vfs,
47 vfs,
48 )
48 )
49
49
50 from .utils import (
50 from .utils import (
51 procutil,
51 procutil,
52 stringutil,
52 stringutil,
53 urlutil,
53 urlutil,
54 )
54 )
55
55
56
56
57 class request(object):
57 class request(object):
58 def __init__(
58 def __init__(
59 self,
59 self,
60 args,
60 args,
61 ui=None,
61 ui=None,
62 repo=None,
62 repo=None,
63 fin=None,
63 fin=None,
64 fout=None,
64 fout=None,
65 ferr=None,
65 ferr=None,
66 fmsg=None,
66 fmsg=None,
67 prereposetups=None,
67 prereposetups=None,
68 ):
68 ):
69 self.args = args
69 self.args = args
70 self.ui = ui
70 self.ui = ui
71 self.repo = repo
71 self.repo = repo
72
72
73 # input/output/error streams
73 # input/output/error streams
74 self.fin = fin
74 self.fin = fin
75 self.fout = fout
75 self.fout = fout
76 self.ferr = ferr
76 self.ferr = ferr
77 # separate stream for status/error messages
77 # separate stream for status/error messages
78 self.fmsg = fmsg
78 self.fmsg = fmsg
79
79
80 # remember options pre-parsed by _earlyparseopts()
80 # remember options pre-parsed by _earlyparseopts()
81 self.earlyoptions = {}
81 self.earlyoptions = {}
82
82
83 # reposetups which run before extensions, useful for chg to pre-fill
83 # reposetups which run before extensions, useful for chg to pre-fill
84 # low-level repo state (for example, changelog) before extensions.
84 # low-level repo state (for example, changelog) before extensions.
85 self.prereposetups = prereposetups or []
85 self.prereposetups = prereposetups or []
86
86
87 # store the parsed and canonical command
87 # store the parsed and canonical command
88 self.canonical_command = None
88 self.canonical_command = None
89
89
90 def _runexithandlers(self):
90 def _runexithandlers(self):
91 exc = None
91 exc = None
92 handlers = self.ui._exithandlers
92 handlers = self.ui._exithandlers
93 try:
93 try:
94 while handlers:
94 while handlers:
95 func, args, kwargs = handlers.pop()
95 func, args, kwargs = handlers.pop()
96 try:
96 try:
97 func(*args, **kwargs)
97 func(*args, **kwargs)
98 except: # re-raises below
98 except: # re-raises below
99 if exc is None:
99 if exc is None:
100 exc = sys.exc_info()[1]
100 exc = sys.exc_info()[1]
101 self.ui.warnnoi18n(b'error in exit handlers:\n')
101 self.ui.warnnoi18n(b'error in exit handlers:\n')
102 self.ui.traceback(force=True)
102 self.ui.traceback(force=True)
103 finally:
103 finally:
104 if exc is not None:
104 if exc is not None:
105 raise exc
105 raise exc
106
106
107
107
108 def _flushstdio(ui, err):
108 def _flushstdio(ui, err):
109 status = None
109 status = None
110 # In all cases we try to flush stdio streams.
110 # In all cases we try to flush stdio streams.
111 if util.safehasattr(ui, b'fout'):
111 if util.safehasattr(ui, b'fout'):
112 assert ui is not None # help pytype
112 assert ui is not None # help pytype
113 assert ui.fout is not None # help pytype
113 assert ui.fout is not None # help pytype
114 try:
114 try:
115 ui.fout.flush()
115 ui.fout.flush()
116 except IOError as e:
116 except IOError as e:
117 err = e
117 err = e
118 status = -1
118 status = -1
119
119
120 if util.safehasattr(ui, b'ferr'):
120 if util.safehasattr(ui, b'ferr'):
121 assert ui is not None # help pytype
121 assert ui is not None # help pytype
122 assert ui.ferr is not None # help pytype
122 assert ui.ferr is not None # help pytype
123 try:
123 try:
124 if err is not None and err.errno != errno.EPIPE:
124 if err is not None and err.errno != errno.EPIPE:
125 ui.ferr.write(
125 ui.ferr.write(
126 b'abort: %s\n' % encoding.strtolocal(err.strerror)
126 b'abort: %s\n' % encoding.strtolocal(err.strerror)
127 )
127 )
128 ui.ferr.flush()
128 ui.ferr.flush()
129 # There's not much we can do about an I/O error here. So (possibly)
129 # There's not much we can do about an I/O error here. So (possibly)
130 # change the status code and move on.
130 # change the status code and move on.
131 except IOError:
131 except IOError:
132 status = -1
132 status = -1
133
133
134 return status
134 return status
135
135
136
136
137 def run():
137 def run():
138 """run the command in sys.argv"""
138 """run the command in sys.argv"""
139 try:
139 try:
140 initstdio()
140 initstdio()
141 with tracing.log('parse args into request'):
141 with tracing.log('parse args into request'):
142 req = request(pycompat.sysargv[1:])
142 req = request(pycompat.sysargv[1:])
143
143
144 status = dispatch(req)
144 status = dispatch(req)
145 _silencestdio()
145 _silencestdio()
146 except KeyboardInterrupt:
146 except KeyboardInterrupt:
147 # Catch early/late KeyboardInterrupt as last ditch. Here nothing will
147 # Catch early/late KeyboardInterrupt as last ditch. Here nothing will
148 # be printed to console to avoid another IOError/KeyboardInterrupt.
148 # be printed to console to avoid another IOError/KeyboardInterrupt.
149 status = -1
149 status = -1
150 sys.exit(status & 255)
150 sys.exit(status & 255)
151
151
152
152
153 if pycompat.ispy3:
153 if pycompat.ispy3:
154
154
155 def initstdio():
155 def initstdio():
156 # stdio streams on Python 3 are io.TextIOWrapper instances proxying another
156 # stdio streams on Python 3 are io.TextIOWrapper instances proxying another
157 # 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
158 # preferred mechanism for writing output (ui.write()) uses io.BufferedWriter
158 # preferred mechanism for writing output (ui.write()) uses io.BufferedWriter
159 # instances, which write to the underlying stdio file descriptor in binary
159 # instances, which write to the underlying stdio file descriptor in binary
160 # 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
161 # 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
162 # preferred line ending is not \n.
162 # preferred line ending is not \n.
163 #
163 #
164 # 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
165 # and sys.stderr. They will inherit the line ending normalization settings,
165 # and sys.stderr. They will inherit the line ending normalization settings,
166 # 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
167 # "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
168 # normalization, ensuring compatibility with our ui type.
168 # normalization, ensuring compatibility with our ui type.
169
169
170 if sys.stdout is not None:
170 if sys.stdout is not None:
171 # write_through is new in Python 3.7.
171 # write_through is new in Python 3.7.
172 kwargs = {
172 kwargs = {
173 "newline": "\n",
173 "newline": "\n",
174 "line_buffering": sys.stdout.line_buffering,
174 "line_buffering": sys.stdout.line_buffering,
175 }
175 }
176 if util.safehasattr(sys.stdout, "write_through"):
176 if util.safehasattr(sys.stdout, "write_through"):
177 # pytype: disable=attribute-error
177 # pytype: disable=attribute-error
178 kwargs["write_through"] = sys.stdout.write_through
178 kwargs["write_through"] = sys.stdout.write_through
179 # pytype: enable=attribute-error
179 # pytype: enable=attribute-error
180 sys.stdout = io.TextIOWrapper(
180 sys.stdout = io.TextIOWrapper(
181 sys.stdout.buffer,
181 sys.stdout.buffer,
182 sys.stdout.encoding,
182 sys.stdout.encoding,
183 sys.stdout.errors,
183 sys.stdout.errors,
184 **kwargs
184 **kwargs
185 )
185 )
186
186
187 if sys.stderr is not None:
187 if sys.stderr is not None:
188 kwargs = {
188 kwargs = {
189 "newline": "\n",
189 "newline": "\n",
190 "line_buffering": sys.stderr.line_buffering,
190 "line_buffering": sys.stderr.line_buffering,
191 }
191 }
192 if util.safehasattr(sys.stderr, "write_through"):
192 if util.safehasattr(sys.stderr, "write_through"):
193 # pytype: disable=attribute-error
193 # pytype: disable=attribute-error
194 kwargs["write_through"] = sys.stderr.write_through
194 kwargs["write_through"] = sys.stderr.write_through
195 # pytype: enable=attribute-error
195 # pytype: enable=attribute-error
196 sys.stderr = io.TextIOWrapper(
196 sys.stderr = io.TextIOWrapper(
197 sys.stderr.buffer,
197 sys.stderr.buffer,
198 sys.stderr.encoding,
198 sys.stderr.encoding,
199 sys.stderr.errors,
199 sys.stderr.errors,
200 **kwargs
200 **kwargs
201 )
201 )
202
202
203 if sys.stdin is not None:
203 if sys.stdin is not None:
204 # No write_through on read-only stream.
204 # No write_through on read-only stream.
205 sys.stdin = io.TextIOWrapper(
205 sys.stdin = io.TextIOWrapper(
206 sys.stdin.buffer,
206 sys.stdin.buffer,
207 sys.stdin.encoding,
207 sys.stdin.encoding,
208 sys.stdin.errors,
208 sys.stdin.errors,
209 # None is universal newlines mode.
209 # None is universal newlines mode.
210 newline=None,
210 newline=None,
211 line_buffering=sys.stdin.line_buffering,
211 line_buffering=sys.stdin.line_buffering,
212 )
212 )
213
213
214 def _silencestdio():
214 def _silencestdio():
215 for fp in (sys.stdout, sys.stderr):
215 for fp in (sys.stdout, sys.stderr):
216 if fp is None:
216 if fp is None:
217 continue
217 continue
218 # Check if the file is okay
218 # Check if the file is okay
219 try:
219 try:
220 fp.flush()
220 fp.flush()
221 continue
221 continue
222 except IOError:
222 except IOError:
223 pass
223 pass
224 # Otherwise mark it as closed to silence "Exception ignored in"
224 # Otherwise mark it as closed to silence "Exception ignored in"
225 # message emitted by the interpreter finalizer.
225 # message emitted by the interpreter finalizer.
226 try:
226 try:
227 fp.close()
227 fp.close()
228 except IOError:
228 except IOError:
229 pass
229 pass
230
230
231
231
232 else:
232 else:
233
233
234 def initstdio():
234 def initstdio():
235 for fp in (sys.stdin, sys.stdout, sys.stderr):
235 for fp in (sys.stdin, sys.stdout, sys.stderr):
236 procutil.setbinary(fp)
236 procutil.setbinary(fp)
237
237
238 def _silencestdio():
238 def _silencestdio():
239 pass
239 pass
240
240
241
241
242 def _formatargs(args):
242 def _formatargs(args):
243 return b' '.join(procutil.shellquote(a) for a in args)
243 return b' '.join(procutil.shellquote(a) for a in args)
244
244
245
245
246 def dispatch(req):
246 def dispatch(req):
247 """run the command specified in req.args; returns an integer status code"""
247 """run the command specified in req.args; returns an integer status code"""
248 err = None
248 err = None
249 try:
249 try:
250 status = _rundispatch(req)
250 status = _rundispatch(req)
251 except error.StdioError as e:
251 except error.StdioError as e:
252 err = e
252 err = e
253 status = -1
253 status = -1
254
254
255 # Somehow we have to catcht he exception here; catching it inside
255 # Somehow we have to catcht he exception here; catching it inside
256 # _flushstdio() doesn't work.
256 # _flushstdio() doesn't work.
257 try:
257 try:
258 ret = _flushstdio(req.ui, err)
258 ret = _flushstdio(req.ui, err)
259 if ret and not status:
259 if ret and not status:
260 status = ret
260 status = ret
261 except BaseException:
261 except BaseException:
262 pass
262 pass
263 return status
263 return status
264
264
265
265
266 def _rundispatch(req):
266 def _rundispatch(req):
267 with tracing.log('dispatch._rundispatch'):
267 with tracing.log('dispatch._rundispatch'):
268 if req.ferr:
268 if req.ferr:
269 ferr = req.ferr
269 ferr = req.ferr
270 elif req.ui:
270 elif req.ui:
271 ferr = req.ui.ferr
271 ferr = req.ui.ferr
272 else:
272 else:
273 ferr = procutil.stderr
273 ferr = procutil.stderr
274
274
275 try:
275 try:
276 if not req.ui:
276 if not req.ui:
277 req.ui = uimod.ui.load()
277 req.ui = uimod.ui.load()
278 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
278 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
279 if req.earlyoptions[b'traceback']:
279 if req.earlyoptions[b'traceback']:
280 req.ui.setconfig(b'ui', b'traceback', b'on', b'--traceback')
280 req.ui.setconfig(b'ui', b'traceback', b'on', b'--traceback')
281
281
282 # set ui streams from the request
282 # set ui streams from the request
283 if req.fin:
283 if req.fin:
284 req.ui.fin = req.fin
284 req.ui.fin = req.fin
285 if req.fout:
285 if req.fout:
286 req.ui.fout = req.fout
286 req.ui.fout = req.fout
287 if req.ferr:
287 if req.ferr:
288 req.ui.ferr = req.ferr
288 req.ui.ferr = req.ferr
289 if req.fmsg:
289 if req.fmsg:
290 req.ui.fmsg = req.fmsg
290 req.ui.fmsg = req.fmsg
291 except error.Abort as inst:
291 except error.Abort as inst:
292 ferr.write(inst.format())
292 ferr.write(inst.format())
293 return -1
293 return -1
294
294
295 msg = _formatargs(req.args)
295 msg = _formatargs(req.args)
296 starttime = util.timer()
296 starttime = util.timer()
297 ret = 1 # default of Python exit code on unhandled exception
297 ret = 1 # default of Python exit code on unhandled exception
298 try:
298 try:
299 ret = _runcatch(req) or 0
299 ret = _runcatch(req) or 0
300 except error.ProgrammingError as inst:
300 except error.ProgrammingError as inst:
301 req.ui.error(_(b'** ProgrammingError: %s\n') % inst)
301 req.ui.error(_(b'** ProgrammingError: %s\n') % inst)
302 if inst.hint:
302 if inst.hint:
303 req.ui.error(_(b'** (%s)\n') % inst.hint)
303 req.ui.error(_(b'** (%s)\n') % inst.hint)
304 raise
304 raise
305 except KeyboardInterrupt as inst:
305 except KeyboardInterrupt as inst:
306 try:
306 try:
307 if isinstance(inst, error.SignalInterrupt):
307 if isinstance(inst, error.SignalInterrupt):
308 msg = _(b"killed!\n")
308 msg = _(b"killed!\n")
309 else:
309 else:
310 msg = _(b"interrupted!\n")
310 msg = _(b"interrupted!\n")
311 req.ui.error(msg)
311 req.ui.error(msg)
312 except error.SignalInterrupt:
312 except error.SignalInterrupt:
313 # maybe pager would quit without consuming all the output, and
313 # maybe pager would quit without consuming all the output, and
314 # SIGPIPE was raised. we cannot print anything in this case.
314 # SIGPIPE was raised. we cannot print anything in this case.
315 pass
315 pass
316 except IOError as inst:
316 except IOError as inst:
317 if inst.errno != errno.EPIPE:
317 if inst.errno != errno.EPIPE:
318 raise
318 raise
319 if req.ui.configbool(b'ui', b'detailed-exit-code'):
320 ret = 250
321 else:
322 ret = -1
319 ret = -1
323 finally:
320 finally:
324 duration = util.timer() - starttime
321 duration = util.timer() - starttime
325 try:
322 try:
326 req.ui.flush() # record blocked times
323 req.ui.flush() # record blocked times
327 except BaseException:
324 except BaseException:
328 pass
325 pass
329 if req.ui.logblockedtimes:
326 if req.ui.logblockedtimes:
330 req.ui._blockedtimes[b'command_duration'] = duration * 1000
327 req.ui._blockedtimes[b'command_duration'] = duration * 1000
331 req.ui.log(
328 req.ui.log(
332 b'uiblocked',
329 b'uiblocked',
333 b'ui blocked ms\n',
330 b'ui blocked ms\n',
334 **pycompat.strkwargs(req.ui._blockedtimes)
331 **pycompat.strkwargs(req.ui._blockedtimes)
335 )
332 )
336 return_code = ret & 255
333 return_code = ret & 255
337 req.ui.log(
334 req.ui.log(
338 b"commandfinish",
335 b"commandfinish",
339 b"%s exited %d after %0.2f seconds\n",
336 b"%s exited %d after %0.2f seconds\n",
340 msg,
337 msg,
341 return_code,
338 return_code,
342 duration,
339 duration,
343 return_code=return_code,
340 return_code=return_code,
344 duration=duration,
341 duration=duration,
345 canonical_command=req.canonical_command,
342 canonical_command=req.canonical_command,
346 )
343 )
347 try:
344 try:
348 req._runexithandlers()
345 req._runexithandlers()
349 except: # exiting, so no re-raises
346 except: # exiting, so no re-raises
350 ret = ret or -1
347 ret = ret or -1
351 # do flush again since ui.log() and exit handlers may write to ui
348 # do flush again since ui.log() and exit handlers may write to ui
352 try:
349 try:
353 req.ui.flush()
350 req.ui.flush()
354 except BaseException:
351 except BaseException:
355 pass
352 pass
356 return ret
353 return ret
357
354
358
355
359 def _runcatch(req):
356 def _runcatch(req):
360 with tracing.log('dispatch._runcatch'):
357 with tracing.log('dispatch._runcatch'):
361
358
362 def catchterm(*args):
359 def catchterm(*args):
363 raise error.SignalInterrupt
360 raise error.SignalInterrupt
364
361
365 ui = req.ui
362 ui = req.ui
366 try:
363 try:
367 for name in b'SIGBREAK', b'SIGHUP', b'SIGTERM':
364 for name in b'SIGBREAK', b'SIGHUP', b'SIGTERM':
368 num = getattr(signal, name, None)
365 num = getattr(signal, name, None)
369 if num:
366 if num:
370 signal.signal(num, catchterm)
367 signal.signal(num, catchterm)
371 except ValueError:
368 except ValueError:
372 pass # happens if called in a thread
369 pass # happens if called in a thread
373
370
374 def _runcatchfunc():
371 def _runcatchfunc():
375 realcmd = None
372 realcmd = None
376 try:
373 try:
377 cmdargs = fancyopts.fancyopts(
374 cmdargs = fancyopts.fancyopts(
378 req.args[:], commands.globalopts, {}
375 req.args[:], commands.globalopts, {}
379 )
376 )
380 cmd = cmdargs[0]
377 cmd = cmdargs[0]
381 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
378 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
382 realcmd = aliases[0]
379 realcmd = aliases[0]
383 except (
380 except (
384 error.UnknownCommand,
381 error.UnknownCommand,
385 error.AmbiguousCommand,
382 error.AmbiguousCommand,
386 IndexError,
383 IndexError,
387 getopt.GetoptError,
384 getopt.GetoptError,
388 ):
385 ):
389 # Don't handle this here. We know the command is
386 # Don't handle this here. We know the command is
390 # invalid, but all we're worried about for now is that
387 # invalid, but all we're worried about for now is that
391 # it's not a command that server operators expect to
388 # it's not a command that server operators expect to
392 # be safe to offer to users in a sandbox.
389 # be safe to offer to users in a sandbox.
393 pass
390 pass
394 if realcmd == b'serve' and b'--stdio' in cmdargs:
391 if realcmd == b'serve' and b'--stdio' in cmdargs:
395 # We want to constrain 'hg serve --stdio' instances pretty
392 # We want to constrain 'hg serve --stdio' instances pretty
396 # closely, as many shared-ssh access tools want to grant
393 # closely, as many shared-ssh access tools want to grant
397 # access to run *only* 'hg -R $repo serve --stdio'. We
394 # access to run *only* 'hg -R $repo serve --stdio'. We
398 # restrict to exactly that set of arguments, and prohibit
395 # restrict to exactly that set of arguments, and prohibit
399 # any repo name that starts with '--' to prevent
396 # any repo name that starts with '--' to prevent
400 # shenanigans wherein a user does something like pass
397 # shenanigans wherein a user does something like pass
401 # --debugger or --config=ui.debugger=1 as a repo
398 # --debugger or --config=ui.debugger=1 as a repo
402 # name. This used to actually run the debugger.
399 # name. This used to actually run the debugger.
403 if (
400 if (
404 len(req.args) != 4
401 len(req.args) != 4
405 or req.args[0] != b'-R'
402 or req.args[0] != b'-R'
406 or req.args[1].startswith(b'--')
403 or req.args[1].startswith(b'--')
407 or req.args[2] != b'serve'
404 or req.args[2] != b'serve'
408 or req.args[3] != b'--stdio'
405 or req.args[3] != b'--stdio'
409 ):
406 ):
410 raise error.Abort(
407 raise error.Abort(
411 _(b'potentially unsafe serve --stdio invocation: %s')
408 _(b'potentially unsafe serve --stdio invocation: %s')
412 % (stringutil.pprint(req.args),)
409 % (stringutil.pprint(req.args),)
413 )
410 )
414
411
415 try:
412 try:
416 debugger = b'pdb'
413 debugger = b'pdb'
417 debugtrace = {b'pdb': pdb.set_trace}
414 debugtrace = {b'pdb': pdb.set_trace}
418 debugmortem = {b'pdb': pdb.post_mortem}
415 debugmortem = {b'pdb': pdb.post_mortem}
419
416
420 # read --config before doing anything else
417 # read --config before doing anything else
421 # (e.g. to change trust settings for reading .hg/hgrc)
418 # (e.g. to change trust settings for reading .hg/hgrc)
422 cfgs = _parseconfig(req.ui, req.earlyoptions[b'config'])
419 cfgs = _parseconfig(req.ui, req.earlyoptions[b'config'])
423
420
424 if req.repo:
421 if req.repo:
425 # copy configs that were passed on the cmdline (--config) to
422 # copy configs that were passed on the cmdline (--config) to
426 # the repo ui
423 # the repo ui
427 for sec, name, val in cfgs:
424 for sec, name, val in cfgs:
428 req.repo.ui.setconfig(
425 req.repo.ui.setconfig(
429 sec, name, val, source=b'--config'
426 sec, name, val, source=b'--config'
430 )
427 )
431
428
432 # developer config: ui.debugger
429 # developer config: ui.debugger
433 debugger = ui.config(b"ui", b"debugger")
430 debugger = ui.config(b"ui", b"debugger")
434 debugmod = pdb
431 debugmod = pdb
435 if not debugger or ui.plain():
432 if not debugger or ui.plain():
436 # if we are in HGPLAIN mode, then disable custom debugging
433 # if we are in HGPLAIN mode, then disable custom debugging
437 debugger = b'pdb'
434 debugger = b'pdb'
438 elif req.earlyoptions[b'debugger']:
435 elif req.earlyoptions[b'debugger']:
439 # This import can be slow for fancy debuggers, so only
436 # This import can be slow for fancy debuggers, so only
440 # do it when absolutely necessary, i.e. when actual
437 # do it when absolutely necessary, i.e. when actual
441 # debugging has been requested
438 # debugging has been requested
442 with demandimport.deactivated():
439 with demandimport.deactivated():
443 try:
440 try:
444 debugmod = __import__(debugger)
441 debugmod = __import__(debugger)
445 except ImportError:
442 except ImportError:
446 pass # Leave debugmod = pdb
443 pass # Leave debugmod = pdb
447
444
448 debugtrace[debugger] = debugmod.set_trace
445 debugtrace[debugger] = debugmod.set_trace
449 debugmortem[debugger] = debugmod.post_mortem
446 debugmortem[debugger] = debugmod.post_mortem
450
447
451 # enter the debugger before command execution
448 # enter the debugger before command execution
452 if req.earlyoptions[b'debugger']:
449 if req.earlyoptions[b'debugger']:
453 ui.warn(
450 ui.warn(
454 _(
451 _(
455 b"entering debugger - "
452 b"entering debugger - "
456 b"type c to continue starting hg or h for help\n"
453 b"type c to continue starting hg or h for help\n"
457 )
454 )
458 )
455 )
459
456
460 if (
457 if (
461 debugger != b'pdb'
458 debugger != b'pdb'
462 and debugtrace[debugger] == debugtrace[b'pdb']
459 and debugtrace[debugger] == debugtrace[b'pdb']
463 ):
460 ):
464 ui.warn(
461 ui.warn(
465 _(
462 _(
466 b"%s debugger specified "
463 b"%s debugger specified "
467 b"but its module was not found\n"
464 b"but its module was not found\n"
468 )
465 )
469 % debugger
466 % debugger
470 )
467 )
471 with demandimport.deactivated():
468 with demandimport.deactivated():
472 debugtrace[debugger]()
469 debugtrace[debugger]()
473 try:
470 try:
474 return _dispatch(req)
471 return _dispatch(req)
475 finally:
472 finally:
476 try:
473 try:
477 ui.flush() # record blocked times
474 ui.flush() # record blocked times
478 except BaseException:
475 except BaseException:
479 pass
476 pass
480 except: # re-raises
477 except: # re-raises
481 # enter the debugger when we hit an exception
478 # enter the debugger when we hit an exception
482 if req.earlyoptions[b'debugger']:
479 if req.earlyoptions[b'debugger']:
483 traceback.print_exc()
480 traceback.print_exc()
484 debugmortem[debugger](sys.exc_info()[2])
481 debugmortem[debugger](sys.exc_info()[2])
485 raise
482 raise
486
483
487 return _callcatch(ui, _runcatchfunc)
484 return _callcatch(ui, _runcatchfunc)
488
485
489
486
490 def _callcatch(ui, func):
487 def _callcatch(ui, func):
491 """like scmutil.callcatch but handles more high-level exceptions about
488 """like scmutil.callcatch but handles more high-level exceptions about
492 config parsing and commands. besides, use handlecommandexception to handle
489 config parsing and commands. besides, use handlecommandexception to handle
493 uncaught exceptions.
490 uncaught exceptions.
494 """
491 """
495 detailed_exit_code = -1
492 detailed_exit_code = -1
496 try:
493 try:
497 return scmutil.callcatch(ui, func)
494 return scmutil.callcatch(ui, func)
498 except error.AmbiguousCommand as inst:
495 except error.AmbiguousCommand as inst:
499 detailed_exit_code = 10
496 detailed_exit_code = 10
500 ui.warn(
497 ui.warn(
501 _(b"hg: command '%s' is ambiguous:\n %s\n")
498 _(b"hg: command '%s' is ambiguous:\n %s\n")
502 % (inst.prefix, b" ".join(inst.matches))
499 % (inst.prefix, b" ".join(inst.matches))
503 )
500 )
504 except error.CommandError as inst:
501 except error.CommandError as inst:
505 detailed_exit_code = 10
502 detailed_exit_code = 10
506 if inst.command:
503 if inst.command:
507 ui.pager(b'help')
504 ui.pager(b'help')
508 msgbytes = pycompat.bytestr(inst.message)
505 msgbytes = pycompat.bytestr(inst.message)
509 ui.warn(_(b"hg %s: %s\n") % (inst.command, msgbytes))
506 ui.warn(_(b"hg %s: %s\n") % (inst.command, msgbytes))
510 commands.help_(ui, inst.command, full=False, command=True)
507 commands.help_(ui, inst.command, full=False, command=True)
511 else:
508 else:
512 ui.warn(_(b"hg: %s\n") % inst.message)
509 ui.warn(_(b"hg: %s\n") % inst.message)
513 ui.warn(_(b"(use 'hg help -v' for a list of global options)\n"))
510 ui.warn(_(b"(use 'hg help -v' for a list of global options)\n"))
514 except error.UnknownCommand as inst:
511 except error.UnknownCommand as inst:
515 detailed_exit_code = 10
512 detailed_exit_code = 10
516 nocmdmsg = _(b"hg: unknown command '%s'\n") % inst.command
513 nocmdmsg = _(b"hg: unknown command '%s'\n") % inst.command
517 try:
514 try:
518 # check if the command is in a disabled extension
515 # check if the command is in a disabled extension
519 # (but don't check for extensions themselves)
516 # (but don't check for extensions themselves)
520 formatted = help.formattedhelp(
517 formatted = help.formattedhelp(
521 ui, commands, inst.command, unknowncmd=True
518 ui, commands, inst.command, unknowncmd=True
522 )
519 )
523 ui.warn(nocmdmsg)
520 ui.warn(nocmdmsg)
524 ui.write(formatted)
521 ui.write(formatted)
525 except (error.UnknownCommand, error.Abort):
522 except (error.UnknownCommand, error.Abort):
526 suggested = False
523 suggested = False
527 if inst.all_commands:
524 if inst.all_commands:
528 sim = error.getsimilar(inst.all_commands, inst.command)
525 sim = error.getsimilar(inst.all_commands, inst.command)
529 if sim:
526 if sim:
530 ui.warn(nocmdmsg)
527 ui.warn(nocmdmsg)
531 ui.warn(b"(%s)\n" % error.similarity_hint(sim))
528 ui.warn(b"(%s)\n" % error.similarity_hint(sim))
532 suggested = True
529 suggested = True
533 if not suggested:
530 if not suggested:
534 ui.warn(nocmdmsg)
531 ui.warn(nocmdmsg)
535 ui.warn(_(b"(use 'hg help' for a list of commands)\n"))
532 ui.warn(_(b"(use 'hg help' for a list of commands)\n"))
536 except IOError:
533 except IOError:
537 raise
534 raise
538 except KeyboardInterrupt:
535 except KeyboardInterrupt:
539 raise
536 raise
540 except: # probably re-raises
537 except: # probably re-raises
541 if not handlecommandexception(ui):
538 if not handlecommandexception(ui):
542 raise
539 raise
543
540
544 if ui.configbool(b'ui', b'detailed-exit-code'):
541 if ui.configbool(b'ui', b'detailed-exit-code'):
545 return detailed_exit_code
542 return detailed_exit_code
546 else:
543 else:
547 return -1
544 return -1
548
545
549
546
550 def aliasargs(fn, givenargs):
547 def aliasargs(fn, givenargs):
551 args = []
548 args = []
552 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
549 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
553 if not util.safehasattr(fn, b'_origfunc'):
550 if not util.safehasattr(fn, b'_origfunc'):
554 args = getattr(fn, 'args', args)
551 args = getattr(fn, 'args', args)
555 if args:
552 if args:
556 cmd = b' '.join(map(procutil.shellquote, args))
553 cmd = b' '.join(map(procutil.shellquote, args))
557
554
558 nums = []
555 nums = []
559
556
560 def replacer(m):
557 def replacer(m):
561 num = int(m.group(1)) - 1
558 num = int(m.group(1)) - 1
562 nums.append(num)
559 nums.append(num)
563 if num < len(givenargs):
560 if num < len(givenargs):
564 return givenargs[num]
561 return givenargs[num]
565 raise error.InputError(_(b'too few arguments for command alias'))
562 raise error.InputError(_(b'too few arguments for command alias'))
566
563
567 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
564 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
568 givenargs = [x for i, x in enumerate(givenargs) if i not in nums]
565 givenargs = [x for i, x in enumerate(givenargs) if i not in nums]
569 args = pycompat.shlexsplit(cmd)
566 args = pycompat.shlexsplit(cmd)
570 return args + givenargs
567 return args + givenargs
571
568
572
569
573 def aliasinterpolate(name, args, cmd):
570 def aliasinterpolate(name, args, cmd):
574 """interpolate args into cmd for shell aliases
571 """interpolate args into cmd for shell aliases
575
572
576 This also handles $0, $@ and "$@".
573 This also handles $0, $@ and "$@".
577 """
574 """
578 # util.interpolate can't deal with "$@" (with quotes) because it's only
575 # util.interpolate can't deal with "$@" (with quotes) because it's only
579 # built to match prefix + patterns.
576 # built to match prefix + patterns.
580 replacemap = {b'$%d' % (i + 1): arg for i, arg in enumerate(args)}
577 replacemap = {b'$%d' % (i + 1): arg for i, arg in enumerate(args)}
581 replacemap[b'$0'] = name
578 replacemap[b'$0'] = name
582 replacemap[b'$$'] = b'$'
579 replacemap[b'$$'] = b'$'
583 replacemap[b'$@'] = b' '.join(args)
580 replacemap[b'$@'] = b' '.join(args)
584 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
581 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
585 # parameters, separated out into words. Emulate the same behavior here by
582 # parameters, separated out into words. Emulate the same behavior here by
586 # quoting the arguments individually. POSIX shells will then typically
583 # quoting the arguments individually. POSIX shells will then typically
587 # tokenize each argument into exactly one word.
584 # tokenize each argument into exactly one word.
588 replacemap[b'"$@"'] = b' '.join(procutil.shellquote(arg) for arg in args)
585 replacemap[b'"$@"'] = b' '.join(procutil.shellquote(arg) for arg in args)
589 # escape '\$' for regex
586 # escape '\$' for regex
590 regex = b'|'.join(replacemap.keys()).replace(b'$', br'\$')
587 regex = b'|'.join(replacemap.keys()).replace(b'$', br'\$')
591 r = re.compile(regex)
588 r = re.compile(regex)
592 return r.sub(lambda x: replacemap[x.group()], cmd)
589 return r.sub(lambda x: replacemap[x.group()], cmd)
593
590
594
591
595 class cmdalias(object):
592 class cmdalias(object):
596 def __init__(self, ui, name, definition, cmdtable, source):
593 def __init__(self, ui, name, definition, cmdtable, source):
597 self.name = self.cmd = name
594 self.name = self.cmd = name
598 self.cmdname = b''
595 self.cmdname = b''
599 self.definition = definition
596 self.definition = definition
600 self.fn = None
597 self.fn = None
601 self.givenargs = []
598 self.givenargs = []
602 self.opts = []
599 self.opts = []
603 self.help = b''
600 self.help = b''
604 self.badalias = None
601 self.badalias = None
605 self.unknowncmd = False
602 self.unknowncmd = False
606 self.source = source
603 self.source = source
607
604
608 try:
605 try:
609 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
606 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
610 for alias, e in pycompat.iteritems(cmdtable):
607 for alias, e in pycompat.iteritems(cmdtable):
611 if e is entry:
608 if e is entry:
612 self.cmd = alias
609 self.cmd = alias
613 break
610 break
614 self.shadows = True
611 self.shadows = True
615 except error.UnknownCommand:
612 except error.UnknownCommand:
616 self.shadows = False
613 self.shadows = False
617
614
618 if not self.definition:
615 if not self.definition:
619 self.badalias = _(b"no definition for alias '%s'") % self.name
616 self.badalias = _(b"no definition for alias '%s'") % self.name
620 return
617 return
621
618
622 if self.definition.startswith(b'!'):
619 if self.definition.startswith(b'!'):
623 shdef = self.definition[1:]
620 shdef = self.definition[1:]
624 self.shell = True
621 self.shell = True
625
622
626 def fn(ui, *args):
623 def fn(ui, *args):
627 env = {b'HG_ARGS': b' '.join((self.name,) + args)}
624 env = {b'HG_ARGS': b' '.join((self.name,) + args)}
628
625
629 def _checkvar(m):
626 def _checkvar(m):
630 if m.groups()[0] == b'$':
627 if m.groups()[0] == b'$':
631 return m.group()
628 return m.group()
632 elif int(m.groups()[0]) <= len(args):
629 elif int(m.groups()[0]) <= len(args):
633 return m.group()
630 return m.group()
634 else:
631 else:
635 ui.debug(
632 ui.debug(
636 b"No argument found for substitution "
633 b"No argument found for substitution "
637 b"of %i variable in alias '%s' definition.\n"
634 b"of %i variable in alias '%s' definition.\n"
638 % (int(m.groups()[0]), self.name)
635 % (int(m.groups()[0]), self.name)
639 )
636 )
640 return b''
637 return b''
641
638
642 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
639 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
643 cmd = aliasinterpolate(self.name, args, cmd)
640 cmd = aliasinterpolate(self.name, args, cmd)
644 return ui.system(
641 return ui.system(
645 cmd, environ=env, blockedtag=b'alias_%s' % self.name
642 cmd, environ=env, blockedtag=b'alias_%s' % self.name
646 )
643 )
647
644
648 self.fn = fn
645 self.fn = fn
649 self.alias = True
646 self.alias = True
650 self._populatehelp(ui, name, shdef, self.fn)
647 self._populatehelp(ui, name, shdef, self.fn)
651 return
648 return
652
649
653 try:
650 try:
654 args = pycompat.shlexsplit(self.definition)
651 args = pycompat.shlexsplit(self.definition)
655 except ValueError as inst:
652 except ValueError as inst:
656 self.badalias = _(b"error in definition for alias '%s': %s") % (
653 self.badalias = _(b"error in definition for alias '%s': %s") % (
657 self.name,
654 self.name,
658 stringutil.forcebytestr(inst),
655 stringutil.forcebytestr(inst),
659 )
656 )
660 return
657 return
661 earlyopts, args = _earlysplitopts(args)
658 earlyopts, args = _earlysplitopts(args)
662 if earlyopts:
659 if earlyopts:
663 self.badalias = _(
660 self.badalias = _(
664 b"error in definition for alias '%s': %s may "
661 b"error in definition for alias '%s': %s may "
665 b"only be given on the command line"
662 b"only be given on the command line"
666 ) % (self.name, b'/'.join(pycompat.ziplist(*earlyopts)[0]))
663 ) % (self.name, b'/'.join(pycompat.ziplist(*earlyopts)[0]))
667 return
664 return
668 self.cmdname = cmd = args.pop(0)
665 self.cmdname = cmd = args.pop(0)
669 self.givenargs = args
666 self.givenargs = args
670
667
671 try:
668 try:
672 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
669 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
673 if len(tableentry) > 2:
670 if len(tableentry) > 2:
674 self.fn, self.opts, cmdhelp = tableentry
671 self.fn, self.opts, cmdhelp = tableentry
675 else:
672 else:
676 self.fn, self.opts = tableentry
673 self.fn, self.opts = tableentry
677 cmdhelp = None
674 cmdhelp = None
678
675
679 self.alias = True
676 self.alias = True
680 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
677 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
681
678
682 except error.UnknownCommand:
679 except error.UnknownCommand:
683 self.badalias = _(
680 self.badalias = _(
684 b"alias '%s' resolves to unknown command '%s'"
681 b"alias '%s' resolves to unknown command '%s'"
685 ) % (
682 ) % (
686 self.name,
683 self.name,
687 cmd,
684 cmd,
688 )
685 )
689 self.unknowncmd = True
686 self.unknowncmd = True
690 except error.AmbiguousCommand:
687 except error.AmbiguousCommand:
691 self.badalias = _(
688 self.badalias = _(
692 b"alias '%s' resolves to ambiguous command '%s'"
689 b"alias '%s' resolves to ambiguous command '%s'"
693 ) % (
690 ) % (
694 self.name,
691 self.name,
695 cmd,
692 cmd,
696 )
693 )
697
694
698 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
695 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
699 # confine strings to be passed to i18n.gettext()
696 # confine strings to be passed to i18n.gettext()
700 cfg = {}
697 cfg = {}
701 for k in (b'doc', b'help', b'category'):
698 for k in (b'doc', b'help', b'category'):
702 v = ui.config(b'alias', b'%s:%s' % (name, k), None)
699 v = ui.config(b'alias', b'%s:%s' % (name, k), None)
703 if v is None:
700 if v is None:
704 continue
701 continue
705 if not encoding.isasciistr(v):
702 if not encoding.isasciistr(v):
706 self.badalias = _(
703 self.badalias = _(
707 b"non-ASCII character in alias definition '%s:%s'"
704 b"non-ASCII character in alias definition '%s:%s'"
708 ) % (name, k)
705 ) % (name, k)
709 return
706 return
710 cfg[k] = v
707 cfg[k] = v
711
708
712 self.help = cfg.get(b'help', defaulthelp or b'')
709 self.help = cfg.get(b'help', defaulthelp or b'')
713 if self.help and self.help.startswith(b"hg " + cmd):
710 if self.help and self.help.startswith(b"hg " + cmd):
714 # drop prefix in old-style help lines so hg shows the alias
711 # drop prefix in old-style help lines so hg shows the alias
715 self.help = self.help[4 + len(cmd) :]
712 self.help = self.help[4 + len(cmd) :]
716
713
717 self.owndoc = b'doc' in cfg
714 self.owndoc = b'doc' in cfg
718 doc = cfg.get(b'doc', pycompat.getdoc(fn))
715 doc = cfg.get(b'doc', pycompat.getdoc(fn))
719 if doc is not None:
716 if doc is not None:
720 doc = pycompat.sysstr(doc)
717 doc = pycompat.sysstr(doc)
721 self.__doc__ = doc
718 self.__doc__ = doc
722
719
723 self.helpcategory = cfg.get(
720 self.helpcategory = cfg.get(
724 b'category', registrar.command.CATEGORY_NONE
721 b'category', registrar.command.CATEGORY_NONE
725 )
722 )
726
723
727 @property
724 @property
728 def args(self):
725 def args(self):
729 args = pycompat.maplist(util.expandpath, self.givenargs)
726 args = pycompat.maplist(util.expandpath, self.givenargs)
730 return aliasargs(self.fn, args)
727 return aliasargs(self.fn, args)
731
728
732 def __getattr__(self, name):
729 def __getattr__(self, name):
733 adefaults = {
730 adefaults = {
734 'norepo': True,
731 'norepo': True,
735 'intents': set(),
732 'intents': set(),
736 'optionalrepo': False,
733 'optionalrepo': False,
737 'inferrepo': False,
734 'inferrepo': False,
738 }
735 }
739 if name not in adefaults:
736 if name not in adefaults:
740 raise AttributeError(name)
737 raise AttributeError(name)
741 if self.badalias or util.safehasattr(self, b'shell'):
738 if self.badalias or util.safehasattr(self, b'shell'):
742 return adefaults[name]
739 return adefaults[name]
743 return getattr(self.fn, name)
740 return getattr(self.fn, name)
744
741
745 def __call__(self, ui, *args, **opts):
742 def __call__(self, ui, *args, **opts):
746 if self.badalias:
743 if self.badalias:
747 hint = None
744 hint = None
748 if self.unknowncmd:
745 if self.unknowncmd:
749 try:
746 try:
750 # check if the command is in a disabled extension
747 # check if the command is in a disabled extension
751 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
748 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
752 hint = _(b"'%s' is provided by '%s' extension") % (cmd, ext)
749 hint = _(b"'%s' is provided by '%s' extension") % (cmd, ext)
753 except error.UnknownCommand:
750 except error.UnknownCommand:
754 pass
751 pass
755 raise error.ConfigError(self.badalias, hint=hint)
752 raise error.ConfigError(self.badalias, hint=hint)
756 if self.shadows:
753 if self.shadows:
757 ui.debug(
754 ui.debug(
758 b"alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)
755 b"alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)
759 )
756 )
760
757
761 ui.log(
758 ui.log(
762 b'commandalias',
759 b'commandalias',
763 b"alias '%s' expands to '%s'\n",
760 b"alias '%s' expands to '%s'\n",
764 self.name,
761 self.name,
765 self.definition,
762 self.definition,
766 )
763 )
767 if util.safehasattr(self, b'shell'):
764 if util.safehasattr(self, b'shell'):
768 return self.fn(ui, *args, **opts)
765 return self.fn(ui, *args, **opts)
769 else:
766 else:
770 try:
767 try:
771 return util.checksignature(self.fn)(ui, *args, **opts)
768 return util.checksignature(self.fn)(ui, *args, **opts)
772 except error.SignatureError:
769 except error.SignatureError:
773 args = b' '.join([self.cmdname] + self.args)
770 args = b' '.join([self.cmdname] + self.args)
774 ui.debug(b"alias '%s' expands to '%s'\n" % (self.name, args))
771 ui.debug(b"alias '%s' expands to '%s'\n" % (self.name, args))
775 raise
772 raise
776
773
777
774
778 class lazyaliasentry(object):
775 class lazyaliasentry(object):
779 """like a typical command entry (func, opts, help), but is lazy"""
776 """like a typical command entry (func, opts, help), but is lazy"""
780
777
781 def __init__(self, ui, name, definition, cmdtable, source):
778 def __init__(self, ui, name, definition, cmdtable, source):
782 self.ui = ui
779 self.ui = ui
783 self.name = name
780 self.name = name
784 self.definition = definition
781 self.definition = definition
785 self.cmdtable = cmdtable.copy()
782 self.cmdtable = cmdtable.copy()
786 self.source = source
783 self.source = source
787 self.alias = True
784 self.alias = True
788
785
789 @util.propertycache
786 @util.propertycache
790 def _aliasdef(self):
787 def _aliasdef(self):
791 return cmdalias(
788 return cmdalias(
792 self.ui, self.name, self.definition, self.cmdtable, self.source
789 self.ui, self.name, self.definition, self.cmdtable, self.source
793 )
790 )
794
791
795 def __getitem__(self, n):
792 def __getitem__(self, n):
796 aliasdef = self._aliasdef
793 aliasdef = self._aliasdef
797 if n == 0:
794 if n == 0:
798 return aliasdef
795 return aliasdef
799 elif n == 1:
796 elif n == 1:
800 return aliasdef.opts
797 return aliasdef.opts
801 elif n == 2:
798 elif n == 2:
802 return aliasdef.help
799 return aliasdef.help
803 else:
800 else:
804 raise IndexError
801 raise IndexError
805
802
806 def __iter__(self):
803 def __iter__(self):
807 for i in range(3):
804 for i in range(3):
808 yield self[i]
805 yield self[i]
809
806
810 def __len__(self):
807 def __len__(self):
811 return 3
808 return 3
812
809
813
810
814 def addaliases(ui, cmdtable):
811 def addaliases(ui, cmdtable):
815 # aliases are processed after extensions have been loaded, so they
812 # aliases are processed after extensions have been loaded, so they
816 # may use extension commands. Aliases can also use other alias definitions,
813 # may use extension commands. Aliases can also use other alias definitions,
817 # but only if they have been defined prior to the current definition.
814 # but only if they have been defined prior to the current definition.
818 for alias, definition in ui.configitems(b'alias', ignoresub=True):
815 for alias, definition in ui.configitems(b'alias', ignoresub=True):
819 try:
816 try:
820 if cmdtable[alias].definition == definition:
817 if cmdtable[alias].definition == definition:
821 continue
818 continue
822 except (KeyError, AttributeError):
819 except (KeyError, AttributeError):
823 # definition might not exist or it might not be a cmdalias
820 # definition might not exist or it might not be a cmdalias
824 pass
821 pass
825
822
826 source = ui.configsource(b'alias', alias)
823 source = ui.configsource(b'alias', alias)
827 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
824 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
828 cmdtable[alias] = entry
825 cmdtable[alias] = entry
829
826
830
827
831 def _parse(ui, args):
828 def _parse(ui, args):
832 options = {}
829 options = {}
833 cmdoptions = {}
830 cmdoptions = {}
834
831
835 try:
832 try:
836 args = fancyopts.fancyopts(args, commands.globalopts, options)
833 args = fancyopts.fancyopts(args, commands.globalopts, options)
837 except getopt.GetoptError as inst:
834 except getopt.GetoptError as inst:
838 raise error.CommandError(None, stringutil.forcebytestr(inst))
835 raise error.CommandError(None, stringutil.forcebytestr(inst))
839
836
840 if args:
837 if args:
841 cmd, args = args[0], args[1:]
838 cmd, args = args[0], args[1:]
842 aliases, entry = cmdutil.findcmd(
839 aliases, entry = cmdutil.findcmd(
843 cmd, commands.table, ui.configbool(b"ui", b"strict")
840 cmd, commands.table, ui.configbool(b"ui", b"strict")
844 )
841 )
845 cmd = aliases[0]
842 cmd = aliases[0]
846 args = aliasargs(entry[0], args)
843 args = aliasargs(entry[0], args)
847 defaults = ui.config(b"defaults", cmd)
844 defaults = ui.config(b"defaults", cmd)
848 if defaults:
845 if defaults:
849 args = (
846 args = (
850 pycompat.maplist(util.expandpath, pycompat.shlexsplit(defaults))
847 pycompat.maplist(util.expandpath, pycompat.shlexsplit(defaults))
851 + args
848 + args
852 )
849 )
853 c = list(entry[1])
850 c = list(entry[1])
854 else:
851 else:
855 cmd = None
852 cmd = None
856 c = []
853 c = []
857
854
858 # combine global options into local
855 # combine global options into local
859 for o in commands.globalopts:
856 for o in commands.globalopts:
860 c.append((o[0], o[1], options[o[1]], o[3]))
857 c.append((o[0], o[1], options[o[1]], o[3]))
861
858
862 try:
859 try:
863 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
860 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
864 except getopt.GetoptError as inst:
861 except getopt.GetoptError as inst:
865 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
862 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
866
863
867 # separate global options back out
864 # separate global options back out
868 for o in commands.globalopts:
865 for o in commands.globalopts:
869 n = o[1]
866 n = o[1]
870 options[n] = cmdoptions[n]
867 options[n] = cmdoptions[n]
871 del cmdoptions[n]
868 del cmdoptions[n]
872
869
873 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
870 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
874
871
875
872
876 def _parseconfig(ui, config):
873 def _parseconfig(ui, config):
877 """parse the --config options from the command line"""
874 """parse the --config options from the command line"""
878 configs = []
875 configs = []
879
876
880 for cfg in config:
877 for cfg in config:
881 try:
878 try:
882 name, value = [cfgelem.strip() for cfgelem in cfg.split(b'=', 1)]
879 name, value = [cfgelem.strip() for cfgelem in cfg.split(b'=', 1)]
883 section, name = name.split(b'.', 1)
880 section, name = name.split(b'.', 1)
884 if not section or not name:
881 if not section or not name:
885 raise IndexError
882 raise IndexError
886 ui.setconfig(section, name, value, b'--config')
883 ui.setconfig(section, name, value, b'--config')
887 configs.append((section, name, value))
884 configs.append((section, name, value))
888 except (IndexError, ValueError):
885 except (IndexError, ValueError):
889 raise error.InputError(
886 raise error.InputError(
890 _(
887 _(
891 b'malformed --config option: %r '
888 b'malformed --config option: %r '
892 b'(use --config section.name=value)'
889 b'(use --config section.name=value)'
893 )
890 )
894 % pycompat.bytestr(cfg)
891 % pycompat.bytestr(cfg)
895 )
892 )
896
893
897 return configs
894 return configs
898
895
899
896
900 def _earlyparseopts(ui, args):
897 def _earlyparseopts(ui, args):
901 options = {}
898 options = {}
902 fancyopts.fancyopts(
899 fancyopts.fancyopts(
903 args,
900 args,
904 commands.globalopts,
901 commands.globalopts,
905 options,
902 options,
906 gnu=not ui.plain(b'strictflags'),
903 gnu=not ui.plain(b'strictflags'),
907 early=True,
904 early=True,
908 optaliases={b'repository': [b'repo']},
905 optaliases={b'repository': [b'repo']},
909 )
906 )
910 return options
907 return options
911
908
912
909
913 def _earlysplitopts(args):
910 def _earlysplitopts(args):
914 """Split args into a list of possible early options and remainder args"""
911 """Split args into a list of possible early options and remainder args"""
915 shortoptions = b'R:'
912 shortoptions = b'R:'
916 # TODO: perhaps 'debugger' should be included
913 # TODO: perhaps 'debugger' should be included
917 longoptions = [b'cwd=', b'repository=', b'repo=', b'config=']
914 longoptions = [b'cwd=', b'repository=', b'repo=', b'config=']
918 return fancyopts.earlygetopt(
915 return fancyopts.earlygetopt(
919 args, shortoptions, longoptions, gnu=True, keepsep=True
916 args, shortoptions, longoptions, gnu=True, keepsep=True
920 )
917 )
921
918
922
919
923 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
920 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
924 # run pre-hook, and abort if it fails
921 # run pre-hook, and abort if it fails
925 hook.hook(
922 hook.hook(
926 lui,
923 lui,
927 repo,
924 repo,
928 b"pre-%s" % cmd,
925 b"pre-%s" % cmd,
929 True,
926 True,
930 args=b" ".join(fullargs),
927 args=b" ".join(fullargs),
931 pats=cmdpats,
928 pats=cmdpats,
932 opts=cmdoptions,
929 opts=cmdoptions,
933 )
930 )
934 try:
931 try:
935 ret = _runcommand(ui, options, cmd, d)
932 ret = _runcommand(ui, options, cmd, d)
936 # run post-hook, passing command result
933 # run post-hook, passing command result
937 hook.hook(
934 hook.hook(
938 lui,
935 lui,
939 repo,
936 repo,
940 b"post-%s" % cmd,
937 b"post-%s" % cmd,
941 False,
938 False,
942 args=b" ".join(fullargs),
939 args=b" ".join(fullargs),
943 result=ret,
940 result=ret,
944 pats=cmdpats,
941 pats=cmdpats,
945 opts=cmdoptions,
942 opts=cmdoptions,
946 )
943 )
947 except Exception:
944 except Exception:
948 # run failure hook and re-raise
945 # run failure hook and re-raise
949 hook.hook(
946 hook.hook(
950 lui,
947 lui,
951 repo,
948 repo,
952 b"fail-%s" % cmd,
949 b"fail-%s" % cmd,
953 False,
950 False,
954 args=b" ".join(fullargs),
951 args=b" ".join(fullargs),
955 pats=cmdpats,
952 pats=cmdpats,
956 opts=cmdoptions,
953 opts=cmdoptions,
957 )
954 )
958 raise
955 raise
959 return ret
956 return ret
960
957
961
958
962 def _readsharedsourceconfig(ui, path):
959 def _readsharedsourceconfig(ui, path):
963 """if the current repository is shared one, this tries to read
960 """if the current repository is shared one, this tries to read
964 .hg/hgrc of shared source if we are in share-safe mode
961 .hg/hgrc of shared source if we are in share-safe mode
965
962
966 Config read is loaded into the ui object passed
963 Config read is loaded into the ui object passed
967
964
968 This should be called before reading .hg/hgrc or the main repo
965 This should be called before reading .hg/hgrc or the main repo
969 as that overrides config set in shared source"""
966 as that overrides config set in shared source"""
970 try:
967 try:
971 with open(os.path.join(path, b".hg", b"requires"), "rb") as fp:
968 with open(os.path.join(path, b".hg", b"requires"), "rb") as fp:
972 requirements = set(fp.read().splitlines())
969 requirements = set(fp.read().splitlines())
973 if not (
970 if not (
974 requirementsmod.SHARESAFE_REQUIREMENT in requirements
971 requirementsmod.SHARESAFE_REQUIREMENT in requirements
975 and requirementsmod.SHARED_REQUIREMENT in requirements
972 and requirementsmod.SHARED_REQUIREMENT in requirements
976 ):
973 ):
977 return
974 return
978 hgvfs = vfs.vfs(os.path.join(path, b".hg"))
975 hgvfs = vfs.vfs(os.path.join(path, b".hg"))
979 sharedvfs = localrepo._getsharedvfs(hgvfs, requirements)
976 sharedvfs = localrepo._getsharedvfs(hgvfs, requirements)
980 root = sharedvfs.base
977 root = sharedvfs.base
981 ui.readconfig(sharedvfs.join(b"hgrc"), root)
978 ui.readconfig(sharedvfs.join(b"hgrc"), root)
982 except IOError:
979 except IOError:
983 pass
980 pass
984
981
985
982
986 def _getlocal(ui, rpath, wd=None):
983 def _getlocal(ui, rpath, wd=None):
987 """Return (path, local ui object) for the given target path.
984 """Return (path, local ui object) for the given target path.
988
985
989 Takes paths in [cwd]/.hg/hgrc into account."
986 Takes paths in [cwd]/.hg/hgrc into account."
990 """
987 """
991 if wd is None:
988 if wd is None:
992 try:
989 try:
993 wd = encoding.getcwd()
990 wd = encoding.getcwd()
994 except OSError as e:
991 except OSError as e:
995 raise error.Abort(
992 raise error.Abort(
996 _(b"error getting current working directory: %s")
993 _(b"error getting current working directory: %s")
997 % encoding.strtolocal(e.strerror)
994 % encoding.strtolocal(e.strerror)
998 )
995 )
999
996
1000 path = cmdutil.findrepo(wd) or b""
997 path = cmdutil.findrepo(wd) or b""
1001 if not path:
998 if not path:
1002 lui = ui
999 lui = ui
1003 else:
1000 else:
1004 lui = ui.copy()
1001 lui = ui.copy()
1005 if rcutil.use_repo_hgrc():
1002 if rcutil.use_repo_hgrc():
1006 _readsharedsourceconfig(lui, path)
1003 _readsharedsourceconfig(lui, path)
1007 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
1004 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
1008 lui.readconfig(os.path.join(path, b".hg", b"hgrc-not-shared"), path)
1005 lui.readconfig(os.path.join(path, b".hg", b"hgrc-not-shared"), path)
1009
1006
1010 if rpath:
1007 if rpath:
1011 path = urlutil.get_clone_path(lui, rpath)[0]
1008 path = urlutil.get_clone_path(lui, rpath)[0]
1012 lui = ui.copy()
1009 lui = ui.copy()
1013 if rcutil.use_repo_hgrc():
1010 if rcutil.use_repo_hgrc():
1014 _readsharedsourceconfig(lui, path)
1011 _readsharedsourceconfig(lui, path)
1015 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
1012 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
1016 lui.readconfig(os.path.join(path, b".hg", b"hgrc-not-shared"), path)
1013 lui.readconfig(os.path.join(path, b".hg", b"hgrc-not-shared"), path)
1017
1014
1018 return path, lui
1015 return path, lui
1019
1016
1020
1017
1021 def _checkshellalias(lui, ui, args):
1018 def _checkshellalias(lui, ui, args):
1022 """Return the function to run the shell alias, if it is required"""
1019 """Return the function to run the shell alias, if it is required"""
1023 options = {}
1020 options = {}
1024
1021
1025 try:
1022 try:
1026 args = fancyopts.fancyopts(args, commands.globalopts, options)
1023 args = fancyopts.fancyopts(args, commands.globalopts, options)
1027 except getopt.GetoptError:
1024 except getopt.GetoptError:
1028 return
1025 return
1029
1026
1030 if not args:
1027 if not args:
1031 return
1028 return
1032
1029
1033 cmdtable = commands.table
1030 cmdtable = commands.table
1034
1031
1035 cmd = args[0]
1032 cmd = args[0]
1036 try:
1033 try:
1037 strict = ui.configbool(b"ui", b"strict")
1034 strict = ui.configbool(b"ui", b"strict")
1038 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
1035 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
1039 except (error.AmbiguousCommand, error.UnknownCommand):
1036 except (error.AmbiguousCommand, error.UnknownCommand):
1040 return
1037 return
1041
1038
1042 cmd = aliases[0]
1039 cmd = aliases[0]
1043 fn = entry[0]
1040 fn = entry[0]
1044
1041
1045 if cmd and util.safehasattr(fn, b'shell'):
1042 if cmd and util.safehasattr(fn, b'shell'):
1046 # shell alias shouldn't receive early options which are consumed by hg
1043 # shell alias shouldn't receive early options which are consumed by hg
1047 _earlyopts, args = _earlysplitopts(args)
1044 _earlyopts, args = _earlysplitopts(args)
1048 d = lambda: fn(ui, *args[1:])
1045 d = lambda: fn(ui, *args[1:])
1049 return lambda: runcommand(
1046 return lambda: runcommand(
1050 lui, None, cmd, args[:1], ui, options, d, [], {}
1047 lui, None, cmd, args[:1], ui, options, d, [], {}
1051 )
1048 )
1052
1049
1053
1050
1054 def _dispatch(req):
1051 def _dispatch(req):
1055 args = req.args
1052 args = req.args
1056 ui = req.ui
1053 ui = req.ui
1057
1054
1058 # check for cwd
1055 # check for cwd
1059 cwd = req.earlyoptions[b'cwd']
1056 cwd = req.earlyoptions[b'cwd']
1060 if cwd:
1057 if cwd:
1061 os.chdir(cwd)
1058 os.chdir(cwd)
1062
1059
1063 rpath = req.earlyoptions[b'repository']
1060 rpath = req.earlyoptions[b'repository']
1064 path, lui = _getlocal(ui, rpath)
1061 path, lui = _getlocal(ui, rpath)
1065
1062
1066 uis = {ui, lui}
1063 uis = {ui, lui}
1067
1064
1068 if req.repo:
1065 if req.repo:
1069 uis.add(req.repo.ui)
1066 uis.add(req.repo.ui)
1070
1067
1071 if (
1068 if (
1072 req.earlyoptions[b'verbose']
1069 req.earlyoptions[b'verbose']
1073 or req.earlyoptions[b'debug']
1070 or req.earlyoptions[b'debug']
1074 or req.earlyoptions[b'quiet']
1071 or req.earlyoptions[b'quiet']
1075 ):
1072 ):
1076 for opt in (b'verbose', b'debug', b'quiet'):
1073 for opt in (b'verbose', b'debug', b'quiet'):
1077 val = pycompat.bytestr(bool(req.earlyoptions[opt]))
1074 val = pycompat.bytestr(bool(req.earlyoptions[opt]))
1078 for ui_ in uis:
1075 for ui_ in uis:
1079 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1076 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1080
1077
1081 if req.earlyoptions[b'profile']:
1078 if req.earlyoptions[b'profile']:
1082 for ui_ in uis:
1079 for ui_ in uis:
1083 ui_.setconfig(b'profiling', b'enabled', b'true', b'--profile')
1080 ui_.setconfig(b'profiling', b'enabled', b'true', b'--profile')
1084 elif req.earlyoptions[b'profile'] is False:
1081 elif req.earlyoptions[b'profile'] is False:
1085 # Check for it being set already, so that we don't pollute the config
1082 # Check for it being set already, so that we don't pollute the config
1086 # with this when using chg in the very common case that it's not
1083 # with this when using chg in the very common case that it's not
1087 # enabled.
1084 # enabled.
1088 if lui.configbool(b'profiling', b'enabled'):
1085 if lui.configbool(b'profiling', b'enabled'):
1089 # Only do this on lui so that `chg foo` with a user config setting
1086 # Only do this on lui so that `chg foo` with a user config setting
1090 # profiling.enabled=1 still shows profiling information (chg will
1087 # profiling.enabled=1 still shows profiling information (chg will
1091 # specify `--no-profile` when `hg serve` is starting up, we don't
1088 # specify `--no-profile` when `hg serve` is starting up, we don't
1092 # want that to propagate to every later invocation).
1089 # want that to propagate to every later invocation).
1093 lui.setconfig(b'profiling', b'enabled', b'false', b'--no-profile')
1090 lui.setconfig(b'profiling', b'enabled', b'false', b'--no-profile')
1094
1091
1095 profile = lui.configbool(b'profiling', b'enabled')
1092 profile = lui.configbool(b'profiling', b'enabled')
1096 with profiling.profile(lui, enabled=profile) as profiler:
1093 with profiling.profile(lui, enabled=profile) as profiler:
1097 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
1094 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
1098 # reposetup
1095 # reposetup
1099 extensions.loadall(lui)
1096 extensions.loadall(lui)
1100 # Propagate any changes to lui.__class__ by extensions
1097 # Propagate any changes to lui.__class__ by extensions
1101 ui.__class__ = lui.__class__
1098 ui.__class__ = lui.__class__
1102
1099
1103 # (uisetup and extsetup are handled in extensions.loadall)
1100 # (uisetup and extsetup are handled in extensions.loadall)
1104
1101
1105 # (reposetup is handled in hg.repository)
1102 # (reposetup is handled in hg.repository)
1106
1103
1107 addaliases(lui, commands.table)
1104 addaliases(lui, commands.table)
1108
1105
1109 # All aliases and commands are completely defined, now.
1106 # All aliases and commands are completely defined, now.
1110 # Check abbreviation/ambiguity of shell alias.
1107 # Check abbreviation/ambiguity of shell alias.
1111 shellaliasfn = _checkshellalias(lui, ui, args)
1108 shellaliasfn = _checkshellalias(lui, ui, args)
1112 if shellaliasfn:
1109 if shellaliasfn:
1113 # no additional configs will be set, set up the ui instances
1110 # no additional configs will be set, set up the ui instances
1114 for ui_ in uis:
1111 for ui_ in uis:
1115 extensions.populateui(ui_)
1112 extensions.populateui(ui_)
1116 return shellaliasfn()
1113 return shellaliasfn()
1117
1114
1118 # check for fallback encoding
1115 # check for fallback encoding
1119 fallback = lui.config(b'ui', b'fallbackencoding')
1116 fallback = lui.config(b'ui', b'fallbackencoding')
1120 if fallback:
1117 if fallback:
1121 encoding.fallbackencoding = fallback
1118 encoding.fallbackencoding = fallback
1122
1119
1123 fullargs = args
1120 fullargs = args
1124 cmd, func, args, options, cmdoptions = _parse(lui, args)
1121 cmd, func, args, options, cmdoptions = _parse(lui, args)
1125
1122
1126 # store the canonical command name in request object for later access
1123 # store the canonical command name in request object for later access
1127 req.canonical_command = cmd
1124 req.canonical_command = cmd
1128
1125
1129 if options[b"config"] != req.earlyoptions[b"config"]:
1126 if options[b"config"] != req.earlyoptions[b"config"]:
1130 raise error.InputError(_(b"option --config may not be abbreviated"))
1127 raise error.InputError(_(b"option --config may not be abbreviated"))
1131 if options[b"cwd"] != req.earlyoptions[b"cwd"]:
1128 if options[b"cwd"] != req.earlyoptions[b"cwd"]:
1132 raise error.InputError(_(b"option --cwd may not be abbreviated"))
1129 raise error.InputError(_(b"option --cwd may not be abbreviated"))
1133 if options[b"repository"] != req.earlyoptions[b"repository"]:
1130 if options[b"repository"] != req.earlyoptions[b"repository"]:
1134 raise error.InputError(
1131 raise error.InputError(
1135 _(
1132 _(
1136 b"option -R has to be separated from other options (e.g. not "
1133 b"option -R has to be separated from other options (e.g. not "
1137 b"-qR) and --repository may only be abbreviated as --repo"
1134 b"-qR) and --repository may only be abbreviated as --repo"
1138 )
1135 )
1139 )
1136 )
1140 if options[b"debugger"] != req.earlyoptions[b"debugger"]:
1137 if options[b"debugger"] != req.earlyoptions[b"debugger"]:
1141 raise error.InputError(
1138 raise error.InputError(
1142 _(b"option --debugger may not be abbreviated")
1139 _(b"option --debugger may not be abbreviated")
1143 )
1140 )
1144 # don't validate --profile/--traceback, which can be enabled from now
1141 # don't validate --profile/--traceback, which can be enabled from now
1145
1142
1146 if options[b"encoding"]:
1143 if options[b"encoding"]:
1147 encoding.encoding = options[b"encoding"]
1144 encoding.encoding = options[b"encoding"]
1148 if options[b"encodingmode"]:
1145 if options[b"encodingmode"]:
1149 encoding.encodingmode = options[b"encodingmode"]
1146 encoding.encodingmode = options[b"encodingmode"]
1150 if options[b"time"]:
1147 if options[b"time"]:
1151
1148
1152 def get_times():
1149 def get_times():
1153 t = os.times()
1150 t = os.times()
1154 if t[4] == 0.0:
1151 if t[4] == 0.0:
1155 # Windows leaves this as zero, so use time.perf_counter()
1152 # Windows leaves this as zero, so use time.perf_counter()
1156 t = (t[0], t[1], t[2], t[3], util.timer())
1153 t = (t[0], t[1], t[2], t[3], util.timer())
1157 return t
1154 return t
1158
1155
1159 s = get_times()
1156 s = get_times()
1160
1157
1161 def print_time():
1158 def print_time():
1162 t = get_times()
1159 t = get_times()
1163 ui.warn(
1160 ui.warn(
1164 _(b"time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n")
1161 _(b"time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n")
1165 % (
1162 % (
1166 t[4] - s[4],
1163 t[4] - s[4],
1167 t[0] - s[0],
1164 t[0] - s[0],
1168 t[2] - s[2],
1165 t[2] - s[2],
1169 t[1] - s[1],
1166 t[1] - s[1],
1170 t[3] - s[3],
1167 t[3] - s[3],
1171 )
1168 )
1172 )
1169 )
1173
1170
1174 ui.atexit(print_time)
1171 ui.atexit(print_time)
1175 if options[b"profile"]:
1172 if options[b"profile"]:
1176 profiler.start()
1173 profiler.start()
1177
1174
1178 # if abbreviated version of this were used, take them in account, now
1175 # if abbreviated version of this were used, take them in account, now
1179 if options[b'verbose'] or options[b'debug'] or options[b'quiet']:
1176 if options[b'verbose'] or options[b'debug'] or options[b'quiet']:
1180 for opt in (b'verbose', b'debug', b'quiet'):
1177 for opt in (b'verbose', b'debug', b'quiet'):
1181 if options[opt] == req.earlyoptions[opt]:
1178 if options[opt] == req.earlyoptions[opt]:
1182 continue
1179 continue
1183 val = pycompat.bytestr(bool(options[opt]))
1180 val = pycompat.bytestr(bool(options[opt]))
1184 for ui_ in uis:
1181 for ui_ in uis:
1185 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1182 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1186
1183
1187 if options[b'traceback']:
1184 if options[b'traceback']:
1188 for ui_ in uis:
1185 for ui_ in uis:
1189 ui_.setconfig(b'ui', b'traceback', b'on', b'--traceback')
1186 ui_.setconfig(b'ui', b'traceback', b'on', b'--traceback')
1190
1187
1191 if options[b'noninteractive']:
1188 if options[b'noninteractive']:
1192 for ui_ in uis:
1189 for ui_ in uis:
1193 ui_.setconfig(b'ui', b'interactive', b'off', b'-y')
1190 ui_.setconfig(b'ui', b'interactive', b'off', b'-y')
1194
1191
1195 if cmdoptions.get(b'insecure', False):
1192 if cmdoptions.get(b'insecure', False):
1196 for ui_ in uis:
1193 for ui_ in uis:
1197 ui_.insecureconnections = True
1194 ui_.insecureconnections = True
1198
1195
1199 # setup color handling before pager, because setting up pager
1196 # setup color handling before pager, because setting up pager
1200 # might cause incorrect console information
1197 # might cause incorrect console information
1201 coloropt = options[b'color']
1198 coloropt = options[b'color']
1202 for ui_ in uis:
1199 for ui_ in uis:
1203 if coloropt:
1200 if coloropt:
1204 ui_.setconfig(b'ui', b'color', coloropt, b'--color')
1201 ui_.setconfig(b'ui', b'color', coloropt, b'--color')
1205 color.setup(ui_)
1202 color.setup(ui_)
1206
1203
1207 if stringutil.parsebool(options[b'pager']):
1204 if stringutil.parsebool(options[b'pager']):
1208 # ui.pager() expects 'internal-always-' prefix in this case
1205 # ui.pager() expects 'internal-always-' prefix in this case
1209 ui.pager(b'internal-always-' + cmd)
1206 ui.pager(b'internal-always-' + cmd)
1210 elif options[b'pager'] != b'auto':
1207 elif options[b'pager'] != b'auto':
1211 for ui_ in uis:
1208 for ui_ in uis:
1212 ui_.disablepager()
1209 ui_.disablepager()
1213
1210
1214 # configs are fully loaded, set up the ui instances
1211 # configs are fully loaded, set up the ui instances
1215 for ui_ in uis:
1212 for ui_ in uis:
1216 extensions.populateui(ui_)
1213 extensions.populateui(ui_)
1217
1214
1218 if options[b'version']:
1215 if options[b'version']:
1219 return commands.version_(ui)
1216 return commands.version_(ui)
1220 if options[b'help']:
1217 if options[b'help']:
1221 return commands.help_(ui, cmd, command=cmd is not None)
1218 return commands.help_(ui, cmd, command=cmd is not None)
1222 elif not cmd:
1219 elif not cmd:
1223 return commands.help_(ui, b'shortlist')
1220 return commands.help_(ui, b'shortlist')
1224
1221
1225 repo = None
1222 repo = None
1226 cmdpats = args[:]
1223 cmdpats = args[:]
1227 assert func is not None # help out pytype
1224 assert func is not None # help out pytype
1228 if not func.norepo:
1225 if not func.norepo:
1229 # use the repo from the request only if we don't have -R
1226 # use the repo from the request only if we don't have -R
1230 if not rpath and not cwd:
1227 if not rpath and not cwd:
1231 repo = req.repo
1228 repo = req.repo
1232
1229
1233 if repo:
1230 if repo:
1234 # set the descriptors of the repo ui to those of ui
1231 # set the descriptors of the repo ui to those of ui
1235 repo.ui.fin = ui.fin
1232 repo.ui.fin = ui.fin
1236 repo.ui.fout = ui.fout
1233 repo.ui.fout = ui.fout
1237 repo.ui.ferr = ui.ferr
1234 repo.ui.ferr = ui.ferr
1238 repo.ui.fmsg = ui.fmsg
1235 repo.ui.fmsg = ui.fmsg
1239 else:
1236 else:
1240 try:
1237 try:
1241 repo = hg.repository(
1238 repo = hg.repository(
1242 ui,
1239 ui,
1243 path=path,
1240 path=path,
1244 presetupfuncs=req.prereposetups,
1241 presetupfuncs=req.prereposetups,
1245 intents=func.intents,
1242 intents=func.intents,
1246 )
1243 )
1247 if not repo.local():
1244 if not repo.local():
1248 raise error.InputError(
1245 raise error.InputError(
1249 _(b"repository '%s' is not local") % path
1246 _(b"repository '%s' is not local") % path
1250 )
1247 )
1251 repo.ui.setconfig(
1248 repo.ui.setconfig(
1252 b"bundle", b"mainreporoot", repo.root, b'repo'
1249 b"bundle", b"mainreporoot", repo.root, b'repo'
1253 )
1250 )
1254 except error.RequirementError:
1251 except error.RequirementError:
1255 raise
1252 raise
1256 except error.RepoError:
1253 except error.RepoError:
1257 if rpath: # invalid -R path
1254 if rpath: # invalid -R path
1258 raise
1255 raise
1259 if not func.optionalrepo:
1256 if not func.optionalrepo:
1260 if func.inferrepo and args and not path:
1257 if func.inferrepo and args and not path:
1261 # try to infer -R from command args
1258 # try to infer -R from command args
1262 repos = pycompat.maplist(cmdutil.findrepo, args)
1259 repos = pycompat.maplist(cmdutil.findrepo, args)
1263 guess = repos[0]
1260 guess = repos[0]
1264 if guess and repos.count(guess) == len(repos):
1261 if guess and repos.count(guess) == len(repos):
1265 req.args = [b'--repository', guess] + fullargs
1262 req.args = [b'--repository', guess] + fullargs
1266 req.earlyoptions[b'repository'] = guess
1263 req.earlyoptions[b'repository'] = guess
1267 return _dispatch(req)
1264 return _dispatch(req)
1268 if not path:
1265 if not path:
1269 raise error.InputError(
1266 raise error.InputError(
1270 _(
1267 _(
1271 b"no repository found in"
1268 b"no repository found in"
1272 b" '%s' (.hg not found)"
1269 b" '%s' (.hg not found)"
1273 )
1270 )
1274 % encoding.getcwd()
1271 % encoding.getcwd()
1275 )
1272 )
1276 raise
1273 raise
1277 if repo:
1274 if repo:
1278 ui = repo.ui
1275 ui = repo.ui
1279 if options[b'hidden']:
1276 if options[b'hidden']:
1280 repo = repo.unfiltered()
1277 repo = repo.unfiltered()
1281 args.insert(0, repo)
1278 args.insert(0, repo)
1282 elif rpath:
1279 elif rpath:
1283 ui.warn(_(b"warning: --repository ignored\n"))
1280 ui.warn(_(b"warning: --repository ignored\n"))
1284
1281
1285 msg = _formatargs(fullargs)
1282 msg = _formatargs(fullargs)
1286 ui.log(b"command", b'%s\n', msg)
1283 ui.log(b"command", b'%s\n', msg)
1287 strcmdopt = pycompat.strkwargs(cmdoptions)
1284 strcmdopt = pycompat.strkwargs(cmdoptions)
1288 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1285 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1289 try:
1286 try:
1290 return runcommand(
1287 return runcommand(
1291 lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions
1288 lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions
1292 )
1289 )
1293 finally:
1290 finally:
1294 if repo and repo != req.repo:
1291 if repo and repo != req.repo:
1295 repo.close()
1292 repo.close()
1296
1293
1297
1294
1298 def _runcommand(ui, options, cmd, cmdfunc):
1295 def _runcommand(ui, options, cmd, cmdfunc):
1299 """Run a command function, possibly with profiling enabled."""
1296 """Run a command function, possibly with profiling enabled."""
1300 try:
1297 try:
1301 with tracing.log("Running %s command" % cmd):
1298 with tracing.log("Running %s command" % cmd):
1302 return cmdfunc()
1299 return cmdfunc()
1303 except error.SignatureError:
1300 except error.SignatureError:
1304 raise error.CommandError(cmd, _(b'invalid arguments'))
1301 raise error.CommandError(cmd, _(b'invalid arguments'))
1305
1302
1306
1303
1307 def _exceptionwarning(ui):
1304 def _exceptionwarning(ui):
1308 """Produce a warning message for the current active exception"""
1305 """Produce a warning message for the current active exception"""
1309
1306
1310 # For compatibility checking, we discard the portion of the hg
1307 # For compatibility checking, we discard the portion of the hg
1311 # version after the + on the assumption that if a "normal
1308 # version after the + on the assumption that if a "normal
1312 # user" is running a build with a + in it the packager
1309 # user" is running a build with a + in it the packager
1313 # probably built from fairly close to a tag and anyone with a
1310 # probably built from fairly close to a tag and anyone with a
1314 # 'make local' copy of hg (where the version number can be out
1311 # 'make local' copy of hg (where the version number can be out
1315 # of date) will be clueful enough to notice the implausible
1312 # of date) will be clueful enough to notice the implausible
1316 # version number and try updating.
1313 # version number and try updating.
1317 ct = util.versiontuple(n=2)
1314 ct = util.versiontuple(n=2)
1318 worst = None, ct, b'', b''
1315 worst = None, ct, b'', b''
1319 if ui.config(b'ui', b'supportcontact') is None:
1316 if ui.config(b'ui', b'supportcontact') is None:
1320 for name, mod in extensions.extensions():
1317 for name, mod in extensions.extensions():
1321 # 'testedwith' should be bytes, but not all extensions are ported
1318 # 'testedwith' should be bytes, but not all extensions are ported
1322 # to py3 and we don't want UnicodeException because of that.
1319 # to py3 and we don't want UnicodeException because of that.
1323 testedwith = stringutil.forcebytestr(
1320 testedwith = stringutil.forcebytestr(
1324 getattr(mod, 'testedwith', b'')
1321 getattr(mod, 'testedwith', b'')
1325 )
1322 )
1326 version = extensions.moduleversion(mod)
1323 version = extensions.moduleversion(mod)
1327 report = getattr(mod, 'buglink', _(b'the extension author.'))
1324 report = getattr(mod, 'buglink', _(b'the extension author.'))
1328 if not testedwith.strip():
1325 if not testedwith.strip():
1329 # We found an untested extension. It's likely the culprit.
1326 # We found an untested extension. It's likely the culprit.
1330 worst = name, b'unknown', report, version
1327 worst = name, b'unknown', report, version
1331 break
1328 break
1332
1329
1333 # Never blame on extensions bundled with Mercurial.
1330 # Never blame on extensions bundled with Mercurial.
1334 if extensions.ismoduleinternal(mod):
1331 if extensions.ismoduleinternal(mod):
1335 continue
1332 continue
1336
1333
1337 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1334 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1338 if ct in tested:
1335 if ct in tested:
1339 continue
1336 continue
1340
1337
1341 lower = [t for t in tested if t < ct]
1338 lower = [t for t in tested if t < ct]
1342 nearest = max(lower or tested)
1339 nearest = max(lower or tested)
1343 if worst[0] is None or nearest < worst[1]:
1340 if worst[0] is None or nearest < worst[1]:
1344 worst = name, nearest, report, version
1341 worst = name, nearest, report, version
1345 if worst[0] is not None:
1342 if worst[0] is not None:
1346 name, testedwith, report, version = worst
1343 name, testedwith, report, version = worst
1347 if not isinstance(testedwith, (bytes, str)):
1344 if not isinstance(testedwith, (bytes, str)):
1348 testedwith = b'.'.join(
1345 testedwith = b'.'.join(
1349 [stringutil.forcebytestr(c) for c in testedwith]
1346 [stringutil.forcebytestr(c) for c in testedwith]
1350 )
1347 )
1351 extver = version or _(b"(version N/A)")
1348 extver = version or _(b"(version N/A)")
1352 warning = _(
1349 warning = _(
1353 b'** Unknown exception encountered with '
1350 b'** Unknown exception encountered with '
1354 b'possibly-broken third-party extension "%s" %s\n'
1351 b'possibly-broken third-party extension "%s" %s\n'
1355 b'** which supports versions %s of Mercurial.\n'
1352 b'** which supports versions %s of Mercurial.\n'
1356 b'** Please disable "%s" and try your action again.\n'
1353 b'** Please disable "%s" and try your action again.\n'
1357 b'** If that fixes the bug please report it to %s\n'
1354 b'** If that fixes the bug please report it to %s\n'
1358 ) % (name, extver, testedwith, name, stringutil.forcebytestr(report))
1355 ) % (name, extver, testedwith, name, stringutil.forcebytestr(report))
1359 else:
1356 else:
1360 bugtracker = ui.config(b'ui', b'supportcontact')
1357 bugtracker = ui.config(b'ui', b'supportcontact')
1361 if bugtracker is None:
1358 if bugtracker is None:
1362 bugtracker = _(b"https://mercurial-scm.org/wiki/BugTracker")
1359 bugtracker = _(b"https://mercurial-scm.org/wiki/BugTracker")
1363 warning = (
1360 warning = (
1364 _(
1361 _(
1365 b"** unknown exception encountered, "
1362 b"** unknown exception encountered, "
1366 b"please report by visiting\n** "
1363 b"please report by visiting\n** "
1367 )
1364 )
1368 + bugtracker
1365 + bugtracker
1369 + b'\n'
1366 + b'\n'
1370 )
1367 )
1371 sysversion = pycompat.sysbytes(sys.version).replace(b'\n', b'')
1368 sysversion = pycompat.sysbytes(sys.version).replace(b'\n', b'')
1372
1369
1373 def ext_with_ver(x):
1370 def ext_with_ver(x):
1374 ext = x[0]
1371 ext = x[0]
1375 ver = extensions.moduleversion(x[1])
1372 ver = extensions.moduleversion(x[1])
1376 if ver:
1373 if ver:
1377 ext += b' ' + ver
1374 ext += b' ' + ver
1378 return ext
1375 return ext
1379
1376
1380 warning += (
1377 warning += (
1381 (_(b"** Python %s\n") % sysversion)
1378 (_(b"** Python %s\n") % sysversion)
1382 + (_(b"** Mercurial Distributed SCM (version %s)\n") % util.version())
1379 + (_(b"** Mercurial Distributed SCM (version %s)\n") % util.version())
1383 + (
1380 + (
1384 _(b"** Extensions loaded: %s\n")
1381 _(b"** Extensions loaded: %s\n")
1385 % b", ".join(
1382 % b", ".join(
1386 [ext_with_ver(x) for x in sorted(extensions.extensions())]
1383 [ext_with_ver(x) for x in sorted(extensions.extensions())]
1387 )
1384 )
1388 )
1385 )
1389 )
1386 )
1390 return warning
1387 return warning
1391
1388
1392
1389
1393 def handlecommandexception(ui):
1390 def handlecommandexception(ui):
1394 """Produce a warning message for broken commands
1391 """Produce a warning message for broken commands
1395
1392
1396 Called when handling an exception; the exception is reraised if
1393 Called when handling an exception; the exception is reraised if
1397 this function returns False, ignored otherwise.
1394 this function returns False, ignored otherwise.
1398 """
1395 """
1399 warning = _exceptionwarning(ui)
1396 warning = _exceptionwarning(ui)
1400 ui.log(
1397 ui.log(
1401 b"commandexception",
1398 b"commandexception",
1402 b"%s\n%s\n",
1399 b"%s\n%s\n",
1403 warning,
1400 warning,
1404 pycompat.sysbytes(traceback.format_exc()),
1401 pycompat.sysbytes(traceback.format_exc()),
1405 )
1402 )
1406 ui.warn(warning)
1403 ui.warn(warning)
1407 return False # re-raise the exception
1404 return False # re-raise the exception
@@ -1,539 +1,539 b''
1 #require chg
1 #require chg
2
2
3 $ mkdir log
3 $ mkdir log
4 $ cp $HGRCPATH $HGRCPATH.unconfigured
4 $ cp $HGRCPATH $HGRCPATH.unconfigured
5 $ cat <<'EOF' >> $HGRCPATH
5 $ cat <<'EOF' >> $HGRCPATH
6 > [cmdserver]
6 > [cmdserver]
7 > log = $TESTTMP/log/server.log
7 > log = $TESTTMP/log/server.log
8 > max-log-files = 1
8 > max-log-files = 1
9 > max-log-size = 10 kB
9 > max-log-size = 10 kB
10 > EOF
10 > EOF
11 $ cp $HGRCPATH $HGRCPATH.orig
11 $ cp $HGRCPATH $HGRCPATH.orig
12
12
13 $ filterlog () {
13 $ filterlog () {
14 > sed -e 's!^[0-9/]* [0-9:]* ([0-9]*)>!YYYY/MM/DD HH:MM:SS (PID)>!' \
14 > sed -e 's!^[0-9/]* [0-9:]* ([0-9]*)>!YYYY/MM/DD HH:MM:SS (PID)>!' \
15 > -e 's!\(setprocname\|received fds\|setenv\): .*!\1: ...!' \
15 > -e 's!\(setprocname\|received fds\|setenv\): .*!\1: ...!' \
16 > -e 's!\(confighash\|mtimehash\) = [0-9a-f]*!\1 = ...!g' \
16 > -e 's!\(confighash\|mtimehash\) = [0-9a-f]*!\1 = ...!g' \
17 > -e 's!\(in \)[0-9.]*s\b!\1 ...s!g' \
17 > -e 's!\(in \)[0-9.]*s\b!\1 ...s!g' \
18 > -e 's!\(pid\)=[0-9]*!\1=...!g' \
18 > -e 's!\(pid\)=[0-9]*!\1=...!g' \
19 > -e 's!\(/server-\)[0-9a-f]*!\1...!g'
19 > -e 's!\(/server-\)[0-9a-f]*!\1...!g'
20 > }
20 > }
21
21
22 init repo
22 init repo
23
23
24 $ chg init foo
24 $ chg init foo
25 $ cd foo
25 $ cd foo
26
26
27 ill-formed config
27 ill-formed config
28
28
29 $ chg status
29 $ chg status
30 $ echo '=brokenconfig' >> $HGRCPATH
30 $ echo '=brokenconfig' >> $HGRCPATH
31 $ chg status
31 $ chg status
32 config error at * =brokenconfig (glob)
32 config error at * =brokenconfig (glob)
33 [30]
33 [30]
34
34
35 $ cp $HGRCPATH.orig $HGRCPATH
35 $ cp $HGRCPATH.orig $HGRCPATH
36
36
37 long socket path
37 long socket path
38
38
39 $ sockpath=$TESTTMP/this/path/should/be/longer/than/one-hundred-and-seven/characters/where/107/is/the/typical/size/limit/of/unix-domain-socket
39 $ sockpath=$TESTTMP/this/path/should/be/longer/than/one-hundred-and-seven/characters/where/107/is/the/typical/size/limit/of/unix-domain-socket
40 $ mkdir -p $sockpath
40 $ mkdir -p $sockpath
41 $ bakchgsockname=$CHGSOCKNAME
41 $ bakchgsockname=$CHGSOCKNAME
42 $ CHGSOCKNAME=$sockpath/server
42 $ CHGSOCKNAME=$sockpath/server
43 $ export CHGSOCKNAME
43 $ export CHGSOCKNAME
44 $ chg root
44 $ chg root
45 $TESTTMP/foo
45 $TESTTMP/foo
46 $ rm -rf $sockpath
46 $ rm -rf $sockpath
47 $ CHGSOCKNAME=$bakchgsockname
47 $ CHGSOCKNAME=$bakchgsockname
48 $ export CHGSOCKNAME
48 $ export CHGSOCKNAME
49
49
50 $ cd ..
50 $ cd ..
51
51
52 editor
52 editor
53 ------
53 ------
54
54
55 $ cat >> pushbuffer.py <<EOF
55 $ cat >> pushbuffer.py <<EOF
56 > def reposetup(ui, repo):
56 > def reposetup(ui, repo):
57 > repo.ui.pushbuffer(subproc=True)
57 > repo.ui.pushbuffer(subproc=True)
58 > EOF
58 > EOF
59
59
60 $ chg init editor
60 $ chg init editor
61 $ cd editor
61 $ cd editor
62
62
63 by default, system() should be redirected to the client:
63 by default, system() should be redirected to the client:
64
64
65 $ touch foo
65 $ touch foo
66 $ CHGDEBUG= HGEDITOR=cat chg ci -Am channeled --edit 2>&1 \
66 $ CHGDEBUG= HGEDITOR=cat chg ci -Am channeled --edit 2>&1 \
67 > | egrep "HG:|run 'cat"
67 > | egrep "HG:|run 'cat"
68 chg: debug: * run 'cat "*"' at '$TESTTMP/editor' (glob)
68 chg: debug: * run 'cat "*"' at '$TESTTMP/editor' (glob)
69 HG: Enter commit message. Lines beginning with 'HG:' are removed.
69 HG: Enter commit message. Lines beginning with 'HG:' are removed.
70 HG: Leave message empty to abort commit.
70 HG: Leave message empty to abort commit.
71 HG: --
71 HG: --
72 HG: user: test
72 HG: user: test
73 HG: branch 'default'
73 HG: branch 'default'
74 HG: added foo
74 HG: added foo
75
75
76 but no redirection should be made if output is captured:
76 but no redirection should be made if output is captured:
77
77
78 $ touch bar
78 $ touch bar
79 $ CHGDEBUG= HGEDITOR=cat chg ci -Am bufferred --edit \
79 $ CHGDEBUG= HGEDITOR=cat chg ci -Am bufferred --edit \
80 > --config extensions.pushbuffer="$TESTTMP/pushbuffer.py" 2>&1 \
80 > --config extensions.pushbuffer="$TESTTMP/pushbuffer.py" 2>&1 \
81 > | egrep "HG:|run 'cat"
81 > | egrep "HG:|run 'cat"
82 [1]
82 [1]
83
83
84 check that commit commands succeeded:
84 check that commit commands succeeded:
85
85
86 $ hg log -T '{rev}:{desc}\n'
86 $ hg log -T '{rev}:{desc}\n'
87 1:bufferred
87 1:bufferred
88 0:channeled
88 0:channeled
89
89
90 $ cd ..
90 $ cd ..
91
91
92 pager
92 pager
93 -----
93 -----
94
94
95 $ cat >> fakepager.py <<EOF
95 $ cat >> fakepager.py <<EOF
96 > import sys
96 > import sys
97 > for line in sys.stdin:
97 > for line in sys.stdin:
98 > sys.stdout.write('paged! %r\n' % line)
98 > sys.stdout.write('paged! %r\n' % line)
99 > EOF
99 > EOF
100
100
101 enable pager extension globally, but spawns the master server with no tty:
101 enable pager extension globally, but spawns the master server with no tty:
102
102
103 $ chg init pager
103 $ chg init pager
104 $ cd pager
104 $ cd pager
105 $ cat >> $HGRCPATH <<EOF
105 $ cat >> $HGRCPATH <<EOF
106 > [extensions]
106 > [extensions]
107 > pager =
107 > pager =
108 > [pager]
108 > [pager]
109 > pager = "$PYTHON" $TESTTMP/fakepager.py
109 > pager = "$PYTHON" $TESTTMP/fakepager.py
110 > EOF
110 > EOF
111 $ chg version > /dev/null
111 $ chg version > /dev/null
112 $ touch foo
112 $ touch foo
113 $ chg ci -qAm foo
113 $ chg ci -qAm foo
114
114
115 pager should be enabled if the attached client has a tty:
115 pager should be enabled if the attached client has a tty:
116
116
117 $ chg log -l1 -q --config ui.formatted=True
117 $ chg log -l1 -q --config ui.formatted=True
118 paged! '0:1f7b0de80e11\n'
118 paged! '0:1f7b0de80e11\n'
119 $ chg log -l1 -q --config ui.formatted=False
119 $ chg log -l1 -q --config ui.formatted=False
120 0:1f7b0de80e11
120 0:1f7b0de80e11
121
121
122 chg waits for pager if runcommand raises
122 chg waits for pager if runcommand raises
123
123
124 $ cat > $TESTTMP/crash.py <<EOF
124 $ cat > $TESTTMP/crash.py <<EOF
125 > from mercurial import registrar
125 > from mercurial import registrar
126 > cmdtable = {}
126 > cmdtable = {}
127 > command = registrar.command(cmdtable)
127 > command = registrar.command(cmdtable)
128 > @command(b'crash')
128 > @command(b'crash')
129 > def pagercrash(ui, repo, *pats, **opts):
129 > def pagercrash(ui, repo, *pats, **opts):
130 > ui.write(b'going to crash\n')
130 > ui.write(b'going to crash\n')
131 > raise Exception('.')
131 > raise Exception('.')
132 > EOF
132 > EOF
133
133
134 $ cat > $TESTTMP/fakepager.py <<EOF
134 $ cat > $TESTTMP/fakepager.py <<EOF
135 > from __future__ import absolute_import
135 > from __future__ import absolute_import
136 > import sys
136 > import sys
137 > import time
137 > import time
138 > for line in iter(sys.stdin.readline, ''):
138 > for line in iter(sys.stdin.readline, ''):
139 > if 'crash' in line: # only interested in lines containing 'crash'
139 > if 'crash' in line: # only interested in lines containing 'crash'
140 > # if chg exits when pager is sleeping (incorrectly), the output
140 > # if chg exits when pager is sleeping (incorrectly), the output
141 > # will be captured by the next test case
141 > # will be captured by the next test case
142 > time.sleep(1)
142 > time.sleep(1)
143 > sys.stdout.write('crash-pager: %s' % line)
143 > sys.stdout.write('crash-pager: %s' % line)
144 > EOF
144 > EOF
145
145
146 $ cat >> .hg/hgrc <<EOF
146 $ cat >> .hg/hgrc <<EOF
147 > [extensions]
147 > [extensions]
148 > crash = $TESTTMP/crash.py
148 > crash = $TESTTMP/crash.py
149 > EOF
149 > EOF
150
150
151 $ chg crash --pager=on --config ui.formatted=True 2>/dev/null
151 $ chg crash --pager=on --config ui.formatted=True 2>/dev/null
152 crash-pager: going to crash
152 crash-pager: going to crash
153 [255]
153 [255]
154
154
155 no stdout data should be printed after pager quits, and the buffered data
155 no stdout data should be printed after pager quits, and the buffered data
156 should never persist (issue6207)
156 should never persist (issue6207)
157
157
158 "killed!" may be printed if terminated by SIGPIPE, which isn't important
158 "killed!" may be printed if terminated by SIGPIPE, which isn't important
159 in this test.
159 in this test.
160
160
161 $ cat > $TESTTMP/bulkwrite.py <<'EOF'
161 $ cat > $TESTTMP/bulkwrite.py <<'EOF'
162 > import time
162 > import time
163 > from mercurial import error, registrar
163 > from mercurial import error, registrar
164 > cmdtable = {}
164 > cmdtable = {}
165 > command = registrar.command(cmdtable)
165 > command = registrar.command(cmdtable)
166 > @command(b'bulkwrite')
166 > @command(b'bulkwrite')
167 > def bulkwrite(ui, repo, *pats, **opts):
167 > def bulkwrite(ui, repo, *pats, **opts):
168 > ui.write(b'going to write massive data\n')
168 > ui.write(b'going to write massive data\n')
169 > ui.flush()
169 > ui.flush()
170 > t = time.time()
170 > t = time.time()
171 > while time.time() - t < 2:
171 > while time.time() - t < 2:
172 > ui.write(b'x' * 1023 + b'\n') # will be interrupted by SIGPIPE
172 > ui.write(b'x' * 1023 + b'\n') # will be interrupted by SIGPIPE
173 > raise error.Abort(b"write() doesn't block")
173 > raise error.Abort(b"write() doesn't block")
174 > EOF
174 > EOF
175
175
176 $ cat > $TESTTMP/fakepager.py <<'EOF'
176 $ cat > $TESTTMP/fakepager.py <<'EOF'
177 > import sys
177 > import sys
178 > import time
178 > import time
179 > sys.stdout.write('paged! %r\n' % sys.stdin.readline())
179 > sys.stdout.write('paged! %r\n' % sys.stdin.readline())
180 > time.sleep(1) # new data will be written
180 > time.sleep(1) # new data will be written
181 > EOF
181 > EOF
182
182
183 $ cat >> .hg/hgrc <<EOF
183 $ cat >> .hg/hgrc <<EOF
184 > [extensions]
184 > [extensions]
185 > bulkwrite = $TESTTMP/bulkwrite.py
185 > bulkwrite = $TESTTMP/bulkwrite.py
186 > EOF
186 > EOF
187
187
188 $ chg bulkwrite --pager=on --color no --config ui.formatted=True
188 $ chg bulkwrite --pager=on --color no --config ui.formatted=True
189 paged! 'going to write massive data\n'
189 paged! 'going to write massive data\n'
190 killed! (?)
190 killed! (?)
191 [250]
191 [255]
192
192
193 $ chg bulkwrite --pager=on --color no --config ui.formatted=True
193 $ chg bulkwrite --pager=on --color no --config ui.formatted=True
194 paged! 'going to write massive data\n'
194 paged! 'going to write massive data\n'
195 killed! (?)
195 killed! (?)
196 [250]
196 [255]
197
197
198 $ cd ..
198 $ cd ..
199
199
200 missing stdio
200 missing stdio
201 -------------
201 -------------
202
202
203 $ CHGDEBUG=1 chg version -q 0<&-
203 $ CHGDEBUG=1 chg version -q 0<&-
204 chg: debug: * stdio fds are missing (glob)
204 chg: debug: * stdio fds are missing (glob)
205 chg: debug: * execute original hg (glob)
205 chg: debug: * execute original hg (glob)
206 Mercurial Distributed SCM * (glob)
206 Mercurial Distributed SCM * (glob)
207
207
208 server lifecycle
208 server lifecycle
209 ----------------
209 ----------------
210
210
211 chg server should be restarted on code change, and old server will shut down
211 chg server should be restarted on code change, and old server will shut down
212 automatically. In this test, we use the following time parameters:
212 automatically. In this test, we use the following time parameters:
213
213
214 - "sleep 1" to make mtime different
214 - "sleep 1" to make mtime different
215 - "sleep 2" to notice mtime change (polling interval is 1 sec)
215 - "sleep 2" to notice mtime change (polling interval is 1 sec)
216
216
217 set up repository with an extension:
217 set up repository with an extension:
218
218
219 $ chg init extreload
219 $ chg init extreload
220 $ cd extreload
220 $ cd extreload
221 $ touch dummyext.py
221 $ touch dummyext.py
222 $ cat <<EOF >> .hg/hgrc
222 $ cat <<EOF >> .hg/hgrc
223 > [extensions]
223 > [extensions]
224 > dummyext = dummyext.py
224 > dummyext = dummyext.py
225 > EOF
225 > EOF
226
226
227 isolate socket directory for stable result:
227 isolate socket directory for stable result:
228
228
229 $ OLDCHGSOCKNAME=$CHGSOCKNAME
229 $ OLDCHGSOCKNAME=$CHGSOCKNAME
230 $ mkdir chgsock
230 $ mkdir chgsock
231 $ CHGSOCKNAME=`pwd`/chgsock/server
231 $ CHGSOCKNAME=`pwd`/chgsock/server
232
232
233 warm up server:
233 warm up server:
234
234
235 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
235 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
236 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
236 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
237
237
238 new server should be started if extension modified:
238 new server should be started if extension modified:
239
239
240 $ sleep 1
240 $ sleep 1
241 $ touch dummyext.py
241 $ touch dummyext.py
242 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
242 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
243 chg: debug: * instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
243 chg: debug: * instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
244 chg: debug: * instruction: reconnect (glob)
244 chg: debug: * instruction: reconnect (glob)
245 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
245 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
246
246
247 old server will shut down, while new server should still be reachable:
247 old server will shut down, while new server should still be reachable:
248
248
249 $ sleep 2
249 $ sleep 2
250 $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)
250 $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)
251
251
252 socket file should never be unlinked by old server:
252 socket file should never be unlinked by old server:
253 (simulates unowned socket by updating mtime, which makes sure server exits
253 (simulates unowned socket by updating mtime, which makes sure server exits
254 at polling cycle)
254 at polling cycle)
255
255
256 $ ls chgsock/server-*
256 $ ls chgsock/server-*
257 chgsock/server-* (glob)
257 chgsock/server-* (glob)
258 $ touch chgsock/server-*
258 $ touch chgsock/server-*
259 $ sleep 2
259 $ sleep 2
260 $ ls chgsock/server-*
260 $ ls chgsock/server-*
261 chgsock/server-* (glob)
261 chgsock/server-* (glob)
262
262
263 since no server is reachable from socket file, new server should be started:
263 since no server is reachable from socket file, new server should be started:
264 (this test makes sure that old server shut down automatically)
264 (this test makes sure that old server shut down automatically)
265
265
266 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
266 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
267 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
267 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
268
268
269 shut down servers and restore environment:
269 shut down servers and restore environment:
270
270
271 $ rm -R chgsock
271 $ rm -R chgsock
272 $ sleep 2
272 $ sleep 2
273 $ CHGSOCKNAME=$OLDCHGSOCKNAME
273 $ CHGSOCKNAME=$OLDCHGSOCKNAME
274 $ cd ..
274 $ cd ..
275
275
276 check that server events are recorded:
276 check that server events are recorded:
277
277
278 $ ls log
278 $ ls log
279 server.log
279 server.log
280 server.log.1
280 server.log.1
281
281
282 print only the last 10 lines, since we aren't sure how many records are
282 print only the last 10 lines, since we aren't sure how many records are
283 preserved (since setprocname isn't available on py3 and pure version,
283 preserved (since setprocname isn't available on py3 and pure version,
284 the 10th-most-recent line is different when using py3):
284 the 10th-most-recent line is different when using py3):
285
285
286 $ cat log/server.log.1 log/server.log | tail -10 | filterlog
286 $ cat log/server.log.1 log/server.log | tail -10 | filterlog
287 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ... (no-setprocname !)
287 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ... (no-setprocname !)
288 YYYY/MM/DD HH:MM:SS (PID)> forked worker process (pid=...)
288 YYYY/MM/DD HH:MM:SS (PID)> forked worker process (pid=...)
289 YYYY/MM/DD HH:MM:SS (PID)> setprocname: ... (setprocname !)
289 YYYY/MM/DD HH:MM:SS (PID)> setprocname: ... (setprocname !)
290 YYYY/MM/DD HH:MM:SS (PID)> received fds: ...
290 YYYY/MM/DD HH:MM:SS (PID)> received fds: ...
291 YYYY/MM/DD HH:MM:SS (PID)> chdir to '$TESTTMP/extreload'
291 YYYY/MM/DD HH:MM:SS (PID)> chdir to '$TESTTMP/extreload'
292 YYYY/MM/DD HH:MM:SS (PID)> setumask 18
292 YYYY/MM/DD HH:MM:SS (PID)> setumask 18
293 YYYY/MM/DD HH:MM:SS (PID)> setenv: ...
293 YYYY/MM/DD HH:MM:SS (PID)> setenv: ...
294 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ...
294 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ...
295 YYYY/MM/DD HH:MM:SS (PID)> validate: []
295 YYYY/MM/DD HH:MM:SS (PID)> validate: []
296 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
296 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
297 YYYY/MM/DD HH:MM:SS (PID)> $TESTTMP/extreload/chgsock/server-... is not owned, exiting.
297 YYYY/MM/DD HH:MM:SS (PID)> $TESTTMP/extreload/chgsock/server-... is not owned, exiting.
298
298
299 global data mutated by schems
299 global data mutated by schems
300 -----------------------------
300 -----------------------------
301
301
302 $ hg init schemes
302 $ hg init schemes
303 $ cd schemes
303 $ cd schemes
304
304
305 initial state
305 initial state
306
306
307 $ cat > .hg/hgrc <<'EOF'
307 $ cat > .hg/hgrc <<'EOF'
308 > [extensions]
308 > [extensions]
309 > schemes =
309 > schemes =
310 > [schemes]
310 > [schemes]
311 > foo = https://foo.example.org/
311 > foo = https://foo.example.org/
312 > EOF
312 > EOF
313 $ hg debugexpandscheme foo://expanded
313 $ hg debugexpandscheme foo://expanded
314 https://foo.example.org/expanded
314 https://foo.example.org/expanded
315 $ hg debugexpandscheme bar://unexpanded
315 $ hg debugexpandscheme bar://unexpanded
316 bar://unexpanded
316 bar://unexpanded
317
317
318 add bar
318 add bar
319
319
320 $ cat > .hg/hgrc <<'EOF'
320 $ cat > .hg/hgrc <<'EOF'
321 > [extensions]
321 > [extensions]
322 > schemes =
322 > schemes =
323 > [schemes]
323 > [schemes]
324 > foo = https://foo.example.org/
324 > foo = https://foo.example.org/
325 > bar = https://bar.example.org/
325 > bar = https://bar.example.org/
326 > EOF
326 > EOF
327 $ hg debugexpandscheme foo://expanded
327 $ hg debugexpandscheme foo://expanded
328 https://foo.example.org/expanded
328 https://foo.example.org/expanded
329 $ hg debugexpandscheme bar://expanded
329 $ hg debugexpandscheme bar://expanded
330 https://bar.example.org/expanded
330 https://bar.example.org/expanded
331
331
332 remove foo
332 remove foo
333
333
334 $ cat > .hg/hgrc <<'EOF'
334 $ cat > .hg/hgrc <<'EOF'
335 > [extensions]
335 > [extensions]
336 > schemes =
336 > schemes =
337 > [schemes]
337 > [schemes]
338 > bar = https://bar.example.org/
338 > bar = https://bar.example.org/
339 > EOF
339 > EOF
340 $ hg debugexpandscheme foo://unexpanded
340 $ hg debugexpandscheme foo://unexpanded
341 foo://unexpanded
341 foo://unexpanded
342 $ hg debugexpandscheme bar://expanded
342 $ hg debugexpandscheme bar://expanded
343 https://bar.example.org/expanded
343 https://bar.example.org/expanded
344
344
345 $ cd ..
345 $ cd ..
346
346
347 repository cache
347 repository cache
348 ----------------
348 ----------------
349
349
350 $ rm log/server.log*
350 $ rm log/server.log*
351 $ cp $HGRCPATH.unconfigured $HGRCPATH
351 $ cp $HGRCPATH.unconfigured $HGRCPATH
352 $ cat <<'EOF' >> $HGRCPATH
352 $ cat <<'EOF' >> $HGRCPATH
353 > [cmdserver]
353 > [cmdserver]
354 > log = $TESTTMP/log/server.log
354 > log = $TESTTMP/log/server.log
355 > max-repo-cache = 1
355 > max-repo-cache = 1
356 > track-log = command, repocache
356 > track-log = command, repocache
357 > EOF
357 > EOF
358
358
359 isolate socket directory for stable result:
359 isolate socket directory for stable result:
360
360
361 $ OLDCHGSOCKNAME=$CHGSOCKNAME
361 $ OLDCHGSOCKNAME=$CHGSOCKNAME
362 $ mkdir chgsock
362 $ mkdir chgsock
363 $ CHGSOCKNAME=`pwd`/chgsock/server
363 $ CHGSOCKNAME=`pwd`/chgsock/server
364
364
365 create empty repo and cache it:
365 create empty repo and cache it:
366
366
367 $ hg init cached
367 $ hg init cached
368 $ hg id -R cached
368 $ hg id -R cached
369 000000000000 tip
369 000000000000 tip
370 $ sleep 1
370 $ sleep 1
371
371
372 modify repo (and cache will be invalidated):
372 modify repo (and cache will be invalidated):
373
373
374 $ touch cached/a
374 $ touch cached/a
375 $ hg ci -R cached -Am 'add a'
375 $ hg ci -R cached -Am 'add a'
376 adding a
376 adding a
377 $ sleep 1
377 $ sleep 1
378
378
379 read cached repo:
379 read cached repo:
380
380
381 $ hg log -R cached
381 $ hg log -R cached
382 changeset: 0:ac82d8b1f7c4
382 changeset: 0:ac82d8b1f7c4
383 tag: tip
383 tag: tip
384 user: test
384 user: test
385 date: Thu Jan 01 00:00:00 1970 +0000
385 date: Thu Jan 01 00:00:00 1970 +0000
386 summary: add a
386 summary: add a
387
387
388 $ sleep 1
388 $ sleep 1
389
389
390 discard cached from LRU cache:
390 discard cached from LRU cache:
391
391
392 $ hg clone cached cached2
392 $ hg clone cached cached2
393 updating to branch default
393 updating to branch default
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
395 $ hg id -R cached2
395 $ hg id -R cached2
396 ac82d8b1f7c4 tip
396 ac82d8b1f7c4 tip
397 $ sleep 1
397 $ sleep 1
398
398
399 read uncached repo:
399 read uncached repo:
400
400
401 $ hg log -R cached
401 $ hg log -R cached
402 changeset: 0:ac82d8b1f7c4
402 changeset: 0:ac82d8b1f7c4
403 tag: tip
403 tag: tip
404 user: test
404 user: test
405 date: Thu Jan 01 00:00:00 1970 +0000
405 date: Thu Jan 01 00:00:00 1970 +0000
406 summary: add a
406 summary: add a
407
407
408 $ sleep 1
408 $ sleep 1
409
409
410 shut down servers and restore environment:
410 shut down servers and restore environment:
411
411
412 $ rm -R chgsock
412 $ rm -R chgsock
413 $ sleep 2
413 $ sleep 2
414 $ CHGSOCKNAME=$OLDCHGSOCKNAME
414 $ CHGSOCKNAME=$OLDCHGSOCKNAME
415
415
416 check server log:
416 check server log:
417
417
418 $ cat log/server.log | filterlog
418 $ cat log/server.log | filterlog
419 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
419 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
420 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...) (?)
420 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...) (?)
421 YYYY/MM/DD HH:MM:SS (PID)> init cached
421 YYYY/MM/DD HH:MM:SS (PID)> init cached
422 YYYY/MM/DD HH:MM:SS (PID)> id -R cached
422 YYYY/MM/DD HH:MM:SS (PID)> id -R cached
423 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
423 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
424 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
424 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
425 YYYY/MM/DD HH:MM:SS (PID)> ci -R cached -Am 'add a'
425 YYYY/MM/DD HH:MM:SS (PID)> ci -R cached -Am 'add a'
426 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
426 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
427 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
427 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
428 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
428 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
429 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
429 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
430 YYYY/MM/DD HH:MM:SS (PID)> clone cached cached2
430 YYYY/MM/DD HH:MM:SS (PID)> clone cached cached2
431 YYYY/MM/DD HH:MM:SS (PID)> id -R cached2
431 YYYY/MM/DD HH:MM:SS (PID)> id -R cached2
432 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached2 (in ...s)
432 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached2 (in ...s)
433 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
433 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
434 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
434 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
435
435
436 Test that chg works (sets to the user's actual LC_CTYPE) even when python
436 Test that chg works (sets to the user's actual LC_CTYPE) even when python
437 "coerces" the locale (py3.7+)
437 "coerces" the locale (py3.7+)
438
438
439 $ cat > $TESTTMP/debugenv.py <<EOF
439 $ cat > $TESTTMP/debugenv.py <<EOF
440 > from mercurial import encoding
440 > from mercurial import encoding
441 > from mercurial import registrar
441 > from mercurial import registrar
442 > cmdtable = {}
442 > cmdtable = {}
443 > command = registrar.command(cmdtable)
443 > command = registrar.command(cmdtable)
444 > @command(b'debugenv', [], b'', norepo=True)
444 > @command(b'debugenv', [], b'', norepo=True)
445 > def debugenv(ui):
445 > def debugenv(ui):
446 > for k in [b'LC_ALL', b'LC_CTYPE', b'LANG']:
446 > for k in [b'LC_ALL', b'LC_CTYPE', b'LANG']:
447 > v = encoding.environ.get(k)
447 > v = encoding.environ.get(k)
448 > if v is not None:
448 > if v is not None:
449 > ui.write(b'%s=%s\n' % (k, encoding.environ[k]))
449 > ui.write(b'%s=%s\n' % (k, encoding.environ[k]))
450 > EOF
450 > EOF
451 (hg keeps python's modified LC_CTYPE, chg doesn't)
451 (hg keeps python's modified LC_CTYPE, chg doesn't)
452 $ (unset LC_ALL; unset LANG; LC_CTYPE= "$CHGHG" \
452 $ (unset LC_ALL; unset LANG; LC_CTYPE= "$CHGHG" \
453 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
453 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
454 LC_CTYPE=C.UTF-8 (py37 !)
454 LC_CTYPE=C.UTF-8 (py37 !)
455 LC_CTYPE= (no-py37 !)
455 LC_CTYPE= (no-py37 !)
456 $ (unset LC_ALL; unset LANG; LC_CTYPE= chg \
456 $ (unset LC_ALL; unset LANG; LC_CTYPE= chg \
457 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
457 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
458 LC_CTYPE=
458 LC_CTYPE=
459 $ (unset LC_ALL; unset LANG; LC_CTYPE=unsupported_value chg \
459 $ (unset LC_ALL; unset LANG; LC_CTYPE=unsupported_value chg \
460 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
460 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
461 *cannot change locale* (glob) (?)
461 *cannot change locale* (glob) (?)
462 LC_CTYPE=unsupported_value
462 LC_CTYPE=unsupported_value
463 $ (unset LC_ALL; unset LANG; LC_CTYPE= chg \
463 $ (unset LC_ALL; unset LANG; LC_CTYPE= chg \
464 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
464 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv)
465 LC_CTYPE=
465 LC_CTYPE=
466 $ LANG= LC_ALL= LC_CTYPE= chg \
466 $ LANG= LC_ALL= LC_CTYPE= chg \
467 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv
467 > --config extensions.debugenv=$TESTTMP/debugenv.py debugenv
468 LC_ALL=
468 LC_ALL=
469 LC_CTYPE=
469 LC_CTYPE=
470 LANG=
470 LANG=
471
471
472 Profiling isn't permanently enabled or carried over between chg invocations that
472 Profiling isn't permanently enabled or carried over between chg invocations that
473 share the same server
473 share the same server
474 $ cp $HGRCPATH.orig $HGRCPATH
474 $ cp $HGRCPATH.orig $HGRCPATH
475 $ hg init $TESTTMP/profiling
475 $ hg init $TESTTMP/profiling
476 $ cd $TESTTMP/profiling
476 $ cd $TESTTMP/profiling
477 $ filteredchg() {
477 $ filteredchg() {
478 > CHGDEBUG=1 chg "$@" 2>&1 | egrep 'Sample count|start cmdserver' || true
478 > CHGDEBUG=1 chg "$@" 2>&1 | egrep 'Sample count|start cmdserver' || true
479 > }
479 > }
480 $ newchg() {
480 $ newchg() {
481 > chg --kill-chg-daemon
481 > chg --kill-chg-daemon
482 > filteredchg "$@" | egrep -v 'start cmdserver' || true
482 > filteredchg "$@" | egrep -v 'start cmdserver' || true
483 > }
483 > }
484 (--profile isn't permanently on just because it was specified when chg was
484 (--profile isn't permanently on just because it was specified when chg was
485 started)
485 started)
486 $ newchg log -r . --profile
486 $ newchg log -r . --profile
487 Sample count: * (glob)
487 Sample count: * (glob)
488 $ filteredchg log -r .
488 $ filteredchg log -r .
489 (enabling profiling via config works, even on the first chg command that starts
489 (enabling profiling via config works, even on the first chg command that starts
490 a cmdserver)
490 a cmdserver)
491 $ cat >> $HGRCPATH <<EOF
491 $ cat >> $HGRCPATH <<EOF
492 > [profiling]
492 > [profiling]
493 > type=stat
493 > type=stat
494 > enabled=1
494 > enabled=1
495 > EOF
495 > EOF
496 $ newchg log -r .
496 $ newchg log -r .
497 Sample count: * (glob)
497 Sample count: * (glob)
498 $ filteredchg log -r .
498 $ filteredchg log -r .
499 Sample count: * (glob)
499 Sample count: * (glob)
500 (test that we aren't accumulating more and more samples each run)
500 (test that we aren't accumulating more and more samples each run)
501 $ cat > $TESTTMP/debugsleep.py <<EOF
501 $ cat > $TESTTMP/debugsleep.py <<EOF
502 > import time
502 > import time
503 > from mercurial import registrar
503 > from mercurial import registrar
504 > cmdtable = {}
504 > cmdtable = {}
505 > command = registrar.command(cmdtable)
505 > command = registrar.command(cmdtable)
506 > @command(b'debugsleep', [], b'', norepo=True)
506 > @command(b'debugsleep', [], b'', norepo=True)
507 > def debugsleep(ui):
507 > def debugsleep(ui):
508 > start = time.time()
508 > start = time.time()
509 > x = 0
509 > x = 0
510 > while time.time() < start + 0.5:
510 > while time.time() < start + 0.5:
511 > time.sleep(.1)
511 > time.sleep(.1)
512 > x += 1
512 > x += 1
513 > ui.status(b'%d debugsleep iterations in %.03fs\n' % (x, time.time() - start))
513 > ui.status(b'%d debugsleep iterations in %.03fs\n' % (x, time.time() - start))
514 > EOF
514 > EOF
515 $ cat >> $HGRCPATH <<EOF
515 $ cat >> $HGRCPATH <<EOF
516 > [extensions]
516 > [extensions]
517 > debugsleep = $TESTTMP/debugsleep.py
517 > debugsleep = $TESTTMP/debugsleep.py
518 > EOF
518 > EOF
519 $ newchg debugsleep > run_1
519 $ newchg debugsleep > run_1
520 $ filteredchg debugsleep > run_2
520 $ filteredchg debugsleep > run_2
521 $ filteredchg debugsleep > run_3
521 $ filteredchg debugsleep > run_3
522 $ filteredchg debugsleep > run_4
522 $ filteredchg debugsleep > run_4
523 FIXME: Run 4 should not be >3x Run 1's number of samples.
523 FIXME: Run 4 should not be >3x Run 1's number of samples.
524 $ "$PYTHON" <<EOF
524 $ "$PYTHON" <<EOF
525 > r1 = int(open("run_1", "r").read().split()[-1])
525 > r1 = int(open("run_1", "r").read().split()[-1])
526 > r4 = int(open("run_4", "r").read().split()[-1])
526 > r4 = int(open("run_4", "r").read().split()[-1])
527 > print("Run 1: %d samples\nRun 4: %d samples\nRun 4 > 3 * Run 1: %s" %
527 > print("Run 1: %d samples\nRun 4: %d samples\nRun 4 > 3 * Run 1: %s" %
528 > (r1, r4, r4 > (r1 * 3)))
528 > (r1, r4, r4 > (r1 * 3)))
529 > EOF
529 > EOF
530 Run 1: * samples (glob)
530 Run 1: * samples (glob)
531 Run 4: * samples (glob)
531 Run 4: * samples (glob)
532 Run 4 > 3 * Run 1: False
532 Run 4 > 3 * Run 1: False
533 (Disabling with --no-profile on the commandline still works, but isn't permanent)
533 (Disabling with --no-profile on the commandline still works, but isn't permanent)
534 $ newchg log -r . --no-profile
534 $ newchg log -r . --no-profile
535 $ filteredchg log -r .
535 $ filteredchg log -r .
536 Sample count: * (glob)
536 Sample count: * (glob)
537 $ filteredchg log -r . --no-profile
537 $ filteredchg log -r . --no-profile
538 $ filteredchg log -r .
538 $ filteredchg log -r .
539 Sample count: * (glob)
539 Sample count: * (glob)
@@ -1,1174 +1,1174 b''
1 #require no-rhg no-chg
1 #require no-rhg no-chg
2
2
3 XXX-RHG this test hangs if `hg` is really `rhg`. This was hidden by the use of
3 XXX-RHG this test hangs if `hg` is really `rhg`. This was hidden by the use of
4 `alias hg=rhg` by run-tests.py. With such alias removed, this test is revealed
4 `alias hg=rhg` by run-tests.py. With such alias removed, this test is revealed
5 buggy. This need to be resolved sooner than later.
5 buggy. This need to be resolved sooner than later.
6
6
7 XXX-CHG this test hangs if `hg` is really `chg`. This was hidden by the use of
7 XXX-CHG this test hangs if `hg` is really `chg`. This was hidden by the use of
8 `alias hg=chg` by run-tests.py. With such alias removed, this test is revealed
8 `alias hg=chg` by run-tests.py. With such alias removed, this test is revealed
9 buggy. This need to be resolved sooner than later.
9 buggy. This need to be resolved sooner than later.
10
10
11 #if windows
11 #if windows
12 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
12 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
13 #else
13 #else
14 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
14 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
15 #endif
15 #endif
16 $ export PYTHONPATH
16 $ export PYTHONPATH
17
17
18 typical client does not want echo-back messages, so test without it:
18 typical client does not want echo-back messages, so test without it:
19
19
20 $ grep -v '^promptecho ' < $HGRCPATH >> $HGRCPATH.new
20 $ grep -v '^promptecho ' < $HGRCPATH >> $HGRCPATH.new
21 $ mv $HGRCPATH.new $HGRCPATH
21 $ mv $HGRCPATH.new $HGRCPATH
22
22
23 $ hg init repo
23 $ hg init repo
24 $ cd repo
24 $ cd repo
25
25
26 >>> from __future__ import absolute_import
26 >>> from __future__ import absolute_import
27 >>> import os
27 >>> import os
28 >>> import sys
28 >>> import sys
29 >>> from hgclient import bprint, check, readchannel, runcommand
29 >>> from hgclient import bprint, check, readchannel, runcommand
30 >>> @check
30 >>> @check
31 ... def hellomessage(server):
31 ... def hellomessage(server):
32 ... ch, data = readchannel(server)
32 ... ch, data = readchannel(server)
33 ... bprint(b'%c, %r' % (ch, data))
33 ... bprint(b'%c, %r' % (ch, data))
34 ... # run an arbitrary command to make sure the next thing the server
34 ... # run an arbitrary command to make sure the next thing the server
35 ... # sends isn't part of the hello message
35 ... # sends isn't part of the hello message
36 ... runcommand(server, [b'id'])
36 ... runcommand(server, [b'id'])
37 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
37 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
38 *** runcommand id
38 *** runcommand id
39 000000000000 tip
39 000000000000 tip
40
40
41 >>> from hgclient import check
41 >>> from hgclient import check
42 >>> @check
42 >>> @check
43 ... def unknowncommand(server):
43 ... def unknowncommand(server):
44 ... server.stdin.write(b'unknowncommand\n')
44 ... server.stdin.write(b'unknowncommand\n')
45 abort: unknown command unknowncommand
45 abort: unknown command unknowncommand
46
46
47 >>> from hgclient import check, readchannel, runcommand
47 >>> from hgclient import check, readchannel, runcommand
48 >>> @check
48 >>> @check
49 ... def checkruncommand(server):
49 ... def checkruncommand(server):
50 ... # hello block
50 ... # hello block
51 ... readchannel(server)
51 ... readchannel(server)
52 ...
52 ...
53 ... # no args
53 ... # no args
54 ... runcommand(server, [])
54 ... runcommand(server, [])
55 ...
55 ...
56 ... # global options
56 ... # global options
57 ... runcommand(server, [b'id', b'--quiet'])
57 ... runcommand(server, [b'id', b'--quiet'])
58 ...
58 ...
59 ... # make sure global options don't stick through requests
59 ... # make sure global options don't stick through requests
60 ... runcommand(server, [b'id'])
60 ... runcommand(server, [b'id'])
61 ...
61 ...
62 ... # --config
62 ... # --config
63 ... runcommand(server, [b'id', b'--config', b'ui.quiet=True'])
63 ... runcommand(server, [b'id', b'--config', b'ui.quiet=True'])
64 ...
64 ...
65 ... # make sure --config doesn't stick
65 ... # make sure --config doesn't stick
66 ... runcommand(server, [b'id'])
66 ... runcommand(server, [b'id'])
67 ...
67 ...
68 ... # negative return code should be masked
68 ... # negative return code should be masked
69 ... runcommand(server, [b'id', b'-runknown'])
69 ... runcommand(server, [b'id', b'-runknown'])
70 *** runcommand
70 *** runcommand
71 Mercurial Distributed SCM
71 Mercurial Distributed SCM
72
72
73 basic commands:
73 basic commands:
74
74
75 add add the specified files on the next commit
75 add add the specified files on the next commit
76 annotate show changeset information by line for each file
76 annotate show changeset information by line for each file
77 clone make a copy of an existing repository
77 clone make a copy of an existing repository
78 commit commit the specified files or all outstanding changes
78 commit commit the specified files or all outstanding changes
79 diff diff repository (or selected files)
79 diff diff repository (or selected files)
80 export dump the header and diffs for one or more changesets
80 export dump the header and diffs for one or more changesets
81 forget forget the specified files on the next commit
81 forget forget the specified files on the next commit
82 init create a new repository in the given directory
82 init create a new repository in the given directory
83 log show revision history of entire repository or files
83 log show revision history of entire repository or files
84 merge merge another revision into working directory
84 merge merge another revision into working directory
85 pull pull changes from the specified source
85 pull pull changes from the specified source
86 push push changes to the specified destination
86 push push changes to the specified destination
87 remove remove the specified files on the next commit
87 remove remove the specified files on the next commit
88 serve start stand-alone webserver
88 serve start stand-alone webserver
89 status show changed files in the working directory
89 status show changed files in the working directory
90 summary summarize working directory state
90 summary summarize working directory state
91 update update working directory (or switch revisions)
91 update update working directory (or switch revisions)
92
92
93 (use 'hg help' for the full list of commands or 'hg -v' for details)
93 (use 'hg help' for the full list of commands or 'hg -v' for details)
94 *** runcommand id --quiet
94 *** runcommand id --quiet
95 000000000000
95 000000000000
96 *** runcommand id
96 *** runcommand id
97 000000000000 tip
97 000000000000 tip
98 *** runcommand id --config ui.quiet=True
98 *** runcommand id --config ui.quiet=True
99 000000000000
99 000000000000
100 *** runcommand id
100 *** runcommand id
101 000000000000 tip
101 000000000000 tip
102 *** runcommand id -runknown
102 *** runcommand id -runknown
103 abort: unknown revision 'unknown'
103 abort: unknown revision 'unknown'
104 [10]
104 [10]
105
105
106 >>> from hgclient import bprint, check, readchannel
106 >>> from hgclient import bprint, check, readchannel
107 >>> @check
107 >>> @check
108 ... def inputeof(server):
108 ... def inputeof(server):
109 ... readchannel(server)
109 ... readchannel(server)
110 ... server.stdin.write(b'runcommand\n')
110 ... server.stdin.write(b'runcommand\n')
111 ... # close stdin while server is waiting for input
111 ... # close stdin while server is waiting for input
112 ... server.stdin.close()
112 ... server.stdin.close()
113 ...
113 ...
114 ... # server exits with 1 if the pipe closed while reading the command
114 ... # server exits with 1 if the pipe closed while reading the command
115 ... bprint(b'server exit code =', b'%d' % server.wait())
115 ... bprint(b'server exit code =', b'%d' % server.wait())
116 server exit code = 1
116 server exit code = 1
117
117
118 >>> from hgclient import check, readchannel, runcommand, stringio
118 >>> from hgclient import check, readchannel, runcommand, stringio
119 >>> @check
119 >>> @check
120 ... def serverinput(server):
120 ... def serverinput(server):
121 ... readchannel(server)
121 ... readchannel(server)
122 ...
122 ...
123 ... patch = b"""
123 ... patch = b"""
124 ... # HG changeset patch
124 ... # HG changeset patch
125 ... # User test
125 ... # User test
126 ... # Date 0 0
126 ... # Date 0 0
127 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
127 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
128 ... # Parent 0000000000000000000000000000000000000000
128 ... # Parent 0000000000000000000000000000000000000000
129 ... 1
129 ... 1
130 ...
130 ...
131 ... diff -r 000000000000 -r c103a3dec114 a
131 ... diff -r 000000000000 -r c103a3dec114 a
132 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
132 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
133 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
133 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
134 ... @@ -0,0 +1,1 @@
134 ... @@ -0,0 +1,1 @@
135 ... +1
135 ... +1
136 ... """
136 ... """
137 ...
137 ...
138 ... runcommand(server, [b'import', b'-'], input=stringio(patch))
138 ... runcommand(server, [b'import', b'-'], input=stringio(patch))
139 ... runcommand(server, [b'log'])
139 ... runcommand(server, [b'log'])
140 *** runcommand import -
140 *** runcommand import -
141 applying patch from stdin
141 applying patch from stdin
142 *** runcommand log
142 *** runcommand log
143 changeset: 0:eff892de26ec
143 changeset: 0:eff892de26ec
144 tag: tip
144 tag: tip
145 user: test
145 user: test
146 date: Thu Jan 01 00:00:00 1970 +0000
146 date: Thu Jan 01 00:00:00 1970 +0000
147 summary: 1
147 summary: 1
148
148
149
149
150 check strict parsing of early options:
150 check strict parsing of early options:
151
151
152 >>> import os
152 >>> import os
153 >>> from hgclient import check, readchannel, runcommand
153 >>> from hgclient import check, readchannel, runcommand
154 >>> os.environ['HGPLAIN'] = '+strictflags'
154 >>> os.environ['HGPLAIN'] = '+strictflags'
155 >>> @check
155 >>> @check
156 ... def cwd(server):
156 ... def cwd(server):
157 ... readchannel(server)
157 ... readchannel(server)
158 ... runcommand(server, [b'log', b'-b', b'--config=alias.log=!echo pwned',
158 ... runcommand(server, [b'log', b'-b', b'--config=alias.log=!echo pwned',
159 ... b'default'])
159 ... b'default'])
160 *** runcommand log -b --config=alias.log=!echo pwned default
160 *** runcommand log -b --config=alias.log=!echo pwned default
161 abort: unknown revision '--config=alias.log=!echo pwned'
161 abort: unknown revision '--config=alias.log=!echo pwned'
162 [255]
162 [255]
163
163
164 check that "histedit --commands=-" can read rules from the input channel:
164 check that "histedit --commands=-" can read rules from the input channel:
165
165
166 >>> from hgclient import check, readchannel, runcommand, stringio
166 >>> from hgclient import check, readchannel, runcommand, stringio
167 >>> @check
167 >>> @check
168 ... def serverinput(server):
168 ... def serverinput(server):
169 ... readchannel(server)
169 ... readchannel(server)
170 ... rules = b'pick eff892de26ec\n'
170 ... rules = b'pick eff892de26ec\n'
171 ... runcommand(server, [b'histedit', b'0', b'--commands=-',
171 ... runcommand(server, [b'histedit', b'0', b'--commands=-',
172 ... b'--config', b'extensions.histedit='],
172 ... b'--config', b'extensions.histedit='],
173 ... input=stringio(rules))
173 ... input=stringio(rules))
174 *** runcommand histedit 0 --commands=- --config extensions.histedit=
174 *** runcommand histedit 0 --commands=- --config extensions.histedit=
175
175
176 check that --cwd doesn't persist between requests:
176 check that --cwd doesn't persist between requests:
177
177
178 $ mkdir foo
178 $ mkdir foo
179 $ touch foo/bar
179 $ touch foo/bar
180 >>> from hgclient import check, readchannel, runcommand
180 >>> from hgclient import check, readchannel, runcommand
181 >>> @check
181 >>> @check
182 ... def cwd(server):
182 ... def cwd(server):
183 ... readchannel(server)
183 ... readchannel(server)
184 ... runcommand(server, [b'--cwd', b'foo', b'st', b'bar'])
184 ... runcommand(server, [b'--cwd', b'foo', b'st', b'bar'])
185 ... runcommand(server, [b'st', b'foo/bar'])
185 ... runcommand(server, [b'st', b'foo/bar'])
186 *** runcommand --cwd foo st bar
186 *** runcommand --cwd foo st bar
187 ? bar
187 ? bar
188 *** runcommand st foo/bar
188 *** runcommand st foo/bar
189 ? foo/bar
189 ? foo/bar
190
190
191 $ rm foo/bar
191 $ rm foo/bar
192
192
193
193
194 check that local configs for the cached repo aren't inherited when -R is used:
194 check that local configs for the cached repo aren't inherited when -R is used:
195
195
196 $ cat <<EOF >> .hg/hgrc
196 $ cat <<EOF >> .hg/hgrc
197 > [ui]
197 > [ui]
198 > foo = bar
198 > foo = bar
199 > EOF
199 > EOF
200
200
201 #if no-extraextensions
201 #if no-extraextensions
202
202
203 >>> from hgclient import check, readchannel, runcommand, sep
203 >>> from hgclient import check, readchannel, runcommand, sep
204 >>> @check
204 >>> @check
205 ... def localhgrc(server):
205 ... def localhgrc(server):
206 ... readchannel(server)
206 ... readchannel(server)
207 ...
207 ...
208 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
208 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
209 ... # show it
209 ... # show it
210 ... runcommand(server, [b'showconfig'], outfilter=sep)
210 ... runcommand(server, [b'showconfig'], outfilter=sep)
211 ...
211 ...
212 ... # but not for this repo
212 ... # but not for this repo
213 ... runcommand(server, [b'init', b'foo'])
213 ... runcommand(server, [b'init', b'foo'])
214 ... runcommand(server, [b'-R', b'foo', b'showconfig', b'ui', b'defaults'])
214 ... runcommand(server, [b'-R', b'foo', b'showconfig', b'ui', b'defaults'])
215 *** runcommand showconfig
215 *** runcommand showconfig
216 bundle.mainreporoot=$TESTTMP/repo
216 bundle.mainreporoot=$TESTTMP/repo
217 chgserver.idletimeout=60
217 chgserver.idletimeout=60
218 devel.all-warnings=true
218 devel.all-warnings=true
219 devel.default-date=0 0
219 devel.default-date=0 0
220 extensions.fsmonitor= (fsmonitor !)
220 extensions.fsmonitor= (fsmonitor !)
221 format.exp-dirstate-v2=1 (dirstate-v2 !)
221 format.exp-dirstate-v2=1 (dirstate-v2 !)
222 largefiles.usercache=$TESTTMP/.cache/largefiles
222 largefiles.usercache=$TESTTMP/.cache/largefiles
223 lfs.usercache=$TESTTMP/.cache/lfs
223 lfs.usercache=$TESTTMP/.cache/lfs
224 ui.slash=True
224 ui.slash=True
225 ui.interactive=False
225 ui.interactive=False
226 ui.detailed-exit-code=True
226 ui.detailed-exit-code=True
227 ui.merge=internal:merge
227 ui.merge=internal:merge
228 ui.mergemarkers=detailed
228 ui.mergemarkers=detailed
229 ui.ssh=* (glob)
229 ui.ssh=* (glob)
230 ui.timeout.warn=15
230 ui.timeout.warn=15
231 ui.foo=bar
231 ui.foo=bar
232 ui.nontty=true
232 ui.nontty=true
233 web.address=localhost
233 web.address=localhost
234 web\.ipv6=(?:True|False) (re)
234 web\.ipv6=(?:True|False) (re)
235 web.server-header=testing stub value
235 web.server-header=testing stub value
236 *** runcommand init foo
236 *** runcommand init foo
237 *** runcommand -R foo showconfig ui defaults
237 *** runcommand -R foo showconfig ui defaults
238 ui.slash=True
238 ui.slash=True
239 ui.interactive=False
239 ui.interactive=False
240 ui.detailed-exit-code=True
240 ui.detailed-exit-code=True
241 ui.merge=internal:merge
241 ui.merge=internal:merge
242 ui.mergemarkers=detailed
242 ui.mergemarkers=detailed
243 ui.ssh=* (glob)
243 ui.ssh=* (glob)
244 ui.timeout.warn=15
244 ui.timeout.warn=15
245 ui.nontty=true
245 ui.nontty=true
246 #endif
246 #endif
247
247
248 $ rm -R foo
248 $ rm -R foo
249
249
250 #if windows
250 #if windows
251 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
251 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
252 #else
252 #else
253 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
253 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
254 #endif
254 #endif
255
255
256 $ cat <<EOF > hook.py
256 $ cat <<EOF > hook.py
257 > import sys
257 > import sys
258 > from hgclient import bprint
258 > from hgclient import bprint
259 > def hook(**args):
259 > def hook(**args):
260 > bprint(b'hook talking')
260 > bprint(b'hook talking')
261 > bprint(b'now try to read something: %r' % sys.stdin.read())
261 > bprint(b'now try to read something: %r' % sys.stdin.read())
262 > EOF
262 > EOF
263
263
264 >>> from hgclient import check, readchannel, runcommand, stringio
264 >>> from hgclient import check, readchannel, runcommand, stringio
265 >>> @check
265 >>> @check
266 ... def hookoutput(server):
266 ... def hookoutput(server):
267 ... readchannel(server)
267 ... readchannel(server)
268 ... runcommand(server, [b'--config',
268 ... runcommand(server, [b'--config',
269 ... b'hooks.pre-identify=python:hook.hook',
269 ... b'hooks.pre-identify=python:hook.hook',
270 ... b'id'],
270 ... b'id'],
271 ... input=stringio(b'some input'))
271 ... input=stringio(b'some input'))
272 *** runcommand --config hooks.pre-identify=python:hook.hook id
272 *** runcommand --config hooks.pre-identify=python:hook.hook id
273 eff892de26ec tip
273 eff892de26ec tip
274 hook talking
274 hook talking
275 now try to read something: ''
275 now try to read something: ''
276
276
277 Clean hook cached version
277 Clean hook cached version
278 $ rm hook.py*
278 $ rm hook.py*
279 $ rm -Rf __pycache__
279 $ rm -Rf __pycache__
280
280
281 $ echo a >> a
281 $ echo a >> a
282 >>> import os
282 >>> import os
283 >>> from hgclient import check, readchannel, runcommand
283 >>> from hgclient import check, readchannel, runcommand
284 >>> @check
284 >>> @check
285 ... def outsidechanges(server):
285 ... def outsidechanges(server):
286 ... readchannel(server)
286 ... readchannel(server)
287 ... runcommand(server, [b'status'])
287 ... runcommand(server, [b'status'])
288 ... os.system('hg ci -Am2')
288 ... os.system('hg ci -Am2')
289 ... runcommand(server, [b'tip'])
289 ... runcommand(server, [b'tip'])
290 ... runcommand(server, [b'status'])
290 ... runcommand(server, [b'status'])
291 *** runcommand status
291 *** runcommand status
292 M a
292 M a
293 *** runcommand tip
293 *** runcommand tip
294 changeset: 1:d3a0a68be6de
294 changeset: 1:d3a0a68be6de
295 tag: tip
295 tag: tip
296 user: test
296 user: test
297 date: Thu Jan 01 00:00:00 1970 +0000
297 date: Thu Jan 01 00:00:00 1970 +0000
298 summary: 2
298 summary: 2
299
299
300 *** runcommand status
300 *** runcommand status
301
301
302 >>> import os
302 >>> import os
303 >>> from hgclient import bprint, check, readchannel, runcommand
303 >>> from hgclient import bprint, check, readchannel, runcommand
304 >>> @check
304 >>> @check
305 ... def bookmarks(server):
305 ... def bookmarks(server):
306 ... readchannel(server)
306 ... readchannel(server)
307 ... runcommand(server, [b'bookmarks'])
307 ... runcommand(server, [b'bookmarks'])
308 ...
308 ...
309 ... # changes .hg/bookmarks
309 ... # changes .hg/bookmarks
310 ... os.system('hg bookmark -i bm1')
310 ... os.system('hg bookmark -i bm1')
311 ... os.system('hg bookmark -i bm2')
311 ... os.system('hg bookmark -i bm2')
312 ... runcommand(server, [b'bookmarks'])
312 ... runcommand(server, [b'bookmarks'])
313 ...
313 ...
314 ... # changes .hg/bookmarks.current
314 ... # changes .hg/bookmarks.current
315 ... os.system('hg upd bm1 -q')
315 ... os.system('hg upd bm1 -q')
316 ... runcommand(server, [b'bookmarks'])
316 ... runcommand(server, [b'bookmarks'])
317 ...
317 ...
318 ... runcommand(server, [b'bookmarks', b'bm3'])
318 ... runcommand(server, [b'bookmarks', b'bm3'])
319 ... f = open('a', 'ab')
319 ... f = open('a', 'ab')
320 ... f.write(b'a\n') and None
320 ... f.write(b'a\n') and None
321 ... f.close()
321 ... f.close()
322 ... runcommand(server, [b'commit', b'-Amm'])
322 ... runcommand(server, [b'commit', b'-Amm'])
323 ... runcommand(server, [b'bookmarks'])
323 ... runcommand(server, [b'bookmarks'])
324 ... bprint(b'')
324 ... bprint(b'')
325 *** runcommand bookmarks
325 *** runcommand bookmarks
326 no bookmarks set
326 no bookmarks set
327 *** runcommand bookmarks
327 *** runcommand bookmarks
328 bm1 1:d3a0a68be6de
328 bm1 1:d3a0a68be6de
329 bm2 1:d3a0a68be6de
329 bm2 1:d3a0a68be6de
330 *** runcommand bookmarks
330 *** runcommand bookmarks
331 * bm1 1:d3a0a68be6de
331 * bm1 1:d3a0a68be6de
332 bm2 1:d3a0a68be6de
332 bm2 1:d3a0a68be6de
333 *** runcommand bookmarks bm3
333 *** runcommand bookmarks bm3
334 *** runcommand commit -Amm
334 *** runcommand commit -Amm
335 *** runcommand bookmarks
335 *** runcommand bookmarks
336 bm1 1:d3a0a68be6de
336 bm1 1:d3a0a68be6de
337 bm2 1:d3a0a68be6de
337 bm2 1:d3a0a68be6de
338 * bm3 2:aef17e88f5f0
338 * bm3 2:aef17e88f5f0
339
339
340
340
341 >>> import os
341 >>> import os
342 >>> from hgclient import check, readchannel, runcommand
342 >>> from hgclient import check, readchannel, runcommand
343 >>> @check
343 >>> @check
344 ... def tagscache(server):
344 ... def tagscache(server):
345 ... readchannel(server)
345 ... readchannel(server)
346 ... runcommand(server, [b'id', b'-t', b'-r', b'0'])
346 ... runcommand(server, [b'id', b'-t', b'-r', b'0'])
347 ... os.system('hg tag -r 0 foo')
347 ... os.system('hg tag -r 0 foo')
348 ... runcommand(server, [b'id', b'-t', b'-r', b'0'])
348 ... runcommand(server, [b'id', b'-t', b'-r', b'0'])
349 *** runcommand id -t -r 0
349 *** runcommand id -t -r 0
350
350
351 *** runcommand id -t -r 0
351 *** runcommand id -t -r 0
352 foo
352 foo
353
353
354 >>> import os
354 >>> import os
355 >>> from hgclient import check, readchannel, runcommand
355 >>> from hgclient import check, readchannel, runcommand
356 >>> @check
356 >>> @check
357 ... def setphase(server):
357 ... def setphase(server):
358 ... readchannel(server)
358 ... readchannel(server)
359 ... runcommand(server, [b'phase', b'-r', b'.'])
359 ... runcommand(server, [b'phase', b'-r', b'.'])
360 ... os.system('hg phase -r . -p')
360 ... os.system('hg phase -r . -p')
361 ... runcommand(server, [b'phase', b'-r', b'.'])
361 ... runcommand(server, [b'phase', b'-r', b'.'])
362 *** runcommand phase -r .
362 *** runcommand phase -r .
363 3: draft
363 3: draft
364 *** runcommand phase -r .
364 *** runcommand phase -r .
365 3: public
365 3: public
366
366
367 $ echo a >> a
367 $ echo a >> a
368 >>> from hgclient import bprint, check, readchannel, runcommand
368 >>> from hgclient import bprint, check, readchannel, runcommand
369 >>> @check
369 >>> @check
370 ... def rollback(server):
370 ... def rollback(server):
371 ... readchannel(server)
371 ... readchannel(server)
372 ... runcommand(server, [b'phase', b'-r', b'.', b'-p'])
372 ... runcommand(server, [b'phase', b'-r', b'.', b'-p'])
373 ... runcommand(server, [b'commit', b'-Am.'])
373 ... runcommand(server, [b'commit', b'-Am.'])
374 ... runcommand(server, [b'rollback'])
374 ... runcommand(server, [b'rollback'])
375 ... runcommand(server, [b'phase', b'-r', b'.'])
375 ... runcommand(server, [b'phase', b'-r', b'.'])
376 ... bprint(b'')
376 ... bprint(b'')
377 *** runcommand phase -r . -p
377 *** runcommand phase -r . -p
378 no phases changed
378 no phases changed
379 *** runcommand commit -Am.
379 *** runcommand commit -Am.
380 *** runcommand rollback
380 *** runcommand rollback
381 repository tip rolled back to revision 3 (undo commit)
381 repository tip rolled back to revision 3 (undo commit)
382 working directory now based on revision 3
382 working directory now based on revision 3
383 *** runcommand phase -r .
383 *** runcommand phase -r .
384 3: public
384 3: public
385
385
386
386
387 >>> import os
387 >>> import os
388 >>> from hgclient import check, readchannel, runcommand
388 >>> from hgclient import check, readchannel, runcommand
389 >>> @check
389 >>> @check
390 ... def branch(server):
390 ... def branch(server):
391 ... readchannel(server)
391 ... readchannel(server)
392 ... runcommand(server, [b'branch'])
392 ... runcommand(server, [b'branch'])
393 ... os.system('hg branch foo')
393 ... os.system('hg branch foo')
394 ... runcommand(server, [b'branch'])
394 ... runcommand(server, [b'branch'])
395 ... os.system('hg branch default')
395 ... os.system('hg branch default')
396 *** runcommand branch
396 *** runcommand branch
397 default
397 default
398 marked working directory as branch foo
398 marked working directory as branch foo
399 (branches are permanent and global, did you want a bookmark?)
399 (branches are permanent and global, did you want a bookmark?)
400 *** runcommand branch
400 *** runcommand branch
401 foo
401 foo
402 marked working directory as branch default
402 marked working directory as branch default
403 (branches are permanent and global, did you want a bookmark?)
403 (branches are permanent and global, did you want a bookmark?)
404
404
405 $ touch .hgignore
405 $ touch .hgignore
406 >>> import os
406 >>> import os
407 >>> from hgclient import bprint, check, readchannel, runcommand
407 >>> from hgclient import bprint, check, readchannel, runcommand
408 >>> @check
408 >>> @check
409 ... def hgignore(server):
409 ... def hgignore(server):
410 ... readchannel(server)
410 ... readchannel(server)
411 ... runcommand(server, [b'commit', b'-Am.'])
411 ... runcommand(server, [b'commit', b'-Am.'])
412 ... f = open('ignored-file', 'ab')
412 ... f = open('ignored-file', 'ab')
413 ... f.write(b'') and None
413 ... f.write(b'') and None
414 ... f.close()
414 ... f.close()
415 ... f = open('.hgignore', 'ab')
415 ... f = open('.hgignore', 'ab')
416 ... f.write(b'ignored-file')
416 ... f.write(b'ignored-file')
417 ... f.close()
417 ... f.close()
418 ... runcommand(server, [b'status', b'-i', b'-u'])
418 ... runcommand(server, [b'status', b'-i', b'-u'])
419 ... bprint(b'')
419 ... bprint(b'')
420 *** runcommand commit -Am.
420 *** runcommand commit -Am.
421 adding .hgignore
421 adding .hgignore
422 *** runcommand status -i -u
422 *** runcommand status -i -u
423 I ignored-file
423 I ignored-file
424
424
425
425
426 cache of non-public revisions should be invalidated on repository change
426 cache of non-public revisions should be invalidated on repository change
427 (issue4855):
427 (issue4855):
428
428
429 >>> import os
429 >>> import os
430 >>> from hgclient import bprint, check, readchannel, runcommand
430 >>> from hgclient import bprint, check, readchannel, runcommand
431 >>> @check
431 >>> @check
432 ... def phasesetscacheaftercommit(server):
432 ... def phasesetscacheaftercommit(server):
433 ... readchannel(server)
433 ... readchannel(server)
434 ... # load _phasecache._phaserevs and _phasesets
434 ... # load _phasecache._phaserevs and _phasesets
435 ... runcommand(server, [b'log', b'-qr', b'draft()'])
435 ... runcommand(server, [b'log', b'-qr', b'draft()'])
436 ... # create draft commits by another process
436 ... # create draft commits by another process
437 ... for i in range(5, 7):
437 ... for i in range(5, 7):
438 ... f = open('a', 'ab')
438 ... f = open('a', 'ab')
439 ... f.seek(0, os.SEEK_END)
439 ... f.seek(0, os.SEEK_END)
440 ... f.write(b'a\n') and None
440 ... f.write(b'a\n') and None
441 ... f.close()
441 ... f.close()
442 ... os.system('hg commit -Aqm%d' % i)
442 ... os.system('hg commit -Aqm%d' % i)
443 ... # new commits should be listed as draft revisions
443 ... # new commits should be listed as draft revisions
444 ... runcommand(server, [b'log', b'-qr', b'draft()'])
444 ... runcommand(server, [b'log', b'-qr', b'draft()'])
445 ... bprint(b'')
445 ... bprint(b'')
446 *** runcommand log -qr draft()
446 *** runcommand log -qr draft()
447 4:7966c8e3734d
447 4:7966c8e3734d
448 *** runcommand log -qr draft()
448 *** runcommand log -qr draft()
449 4:7966c8e3734d
449 4:7966c8e3734d
450 5:41f6602d1c4f
450 5:41f6602d1c4f
451 6:10501e202c35
451 6:10501e202c35
452
452
453
453
454 >>> import os
454 >>> import os
455 >>> from hgclient import bprint, check, readchannel, runcommand
455 >>> from hgclient import bprint, check, readchannel, runcommand
456 >>> @check
456 >>> @check
457 ... def phasesetscacheafterstrip(server):
457 ... def phasesetscacheafterstrip(server):
458 ... readchannel(server)
458 ... readchannel(server)
459 ... # load _phasecache._phaserevs and _phasesets
459 ... # load _phasecache._phaserevs and _phasesets
460 ... runcommand(server, [b'log', b'-qr', b'draft()'])
460 ... runcommand(server, [b'log', b'-qr', b'draft()'])
461 ... # strip cached revisions by another process
461 ... # strip cached revisions by another process
462 ... os.system('hg --config extensions.strip= strip -q 5')
462 ... os.system('hg --config extensions.strip= strip -q 5')
463 ... # shouldn't abort by "unknown revision '6'"
463 ... # shouldn't abort by "unknown revision '6'"
464 ... runcommand(server, [b'log', b'-qr', b'draft()'])
464 ... runcommand(server, [b'log', b'-qr', b'draft()'])
465 ... bprint(b'')
465 ... bprint(b'')
466 *** runcommand log -qr draft()
466 *** runcommand log -qr draft()
467 4:7966c8e3734d
467 4:7966c8e3734d
468 5:41f6602d1c4f
468 5:41f6602d1c4f
469 6:10501e202c35
469 6:10501e202c35
470 *** runcommand log -qr draft()
470 *** runcommand log -qr draft()
471 4:7966c8e3734d
471 4:7966c8e3734d
472
472
473
473
474 cache of phase roots should be invalidated on strip (issue3827):
474 cache of phase roots should be invalidated on strip (issue3827):
475
475
476 >>> import os
476 >>> import os
477 >>> from hgclient import check, readchannel, runcommand, sep
477 >>> from hgclient import check, readchannel, runcommand, sep
478 >>> @check
478 >>> @check
479 ... def phasecacheafterstrip(server):
479 ... def phasecacheafterstrip(server):
480 ... readchannel(server)
480 ... readchannel(server)
481 ...
481 ...
482 ... # create new head, 5:731265503d86
482 ... # create new head, 5:731265503d86
483 ... runcommand(server, [b'update', b'-C', b'0'])
483 ... runcommand(server, [b'update', b'-C', b'0'])
484 ... f = open('a', 'ab')
484 ... f = open('a', 'ab')
485 ... f.write(b'a\n') and None
485 ... f.write(b'a\n') and None
486 ... f.close()
486 ... f.close()
487 ... runcommand(server, [b'commit', b'-Am.', b'a'])
487 ... runcommand(server, [b'commit', b'-Am.', b'a'])
488 ... runcommand(server, [b'log', b'-Gq'])
488 ... runcommand(server, [b'log', b'-Gq'])
489 ...
489 ...
490 ... # make it public; draft marker moves to 4:7966c8e3734d
490 ... # make it public; draft marker moves to 4:7966c8e3734d
491 ... runcommand(server, [b'phase', b'-p', b'.'])
491 ... runcommand(server, [b'phase', b'-p', b'.'])
492 ... # load _phasecache.phaseroots
492 ... # load _phasecache.phaseroots
493 ... runcommand(server, [b'phase', b'.'], outfilter=sep)
493 ... runcommand(server, [b'phase', b'.'], outfilter=sep)
494 ...
494 ...
495 ... # strip 1::4 outside server
495 ... # strip 1::4 outside server
496 ... os.system('hg -q --config extensions.mq= strip 1')
496 ... os.system('hg -q --config extensions.mq= strip 1')
497 ...
497 ...
498 ... # shouldn't raise "7966c8e3734d: no node!"
498 ... # shouldn't raise "7966c8e3734d: no node!"
499 ... runcommand(server, [b'branches'])
499 ... runcommand(server, [b'branches'])
500 *** runcommand update -C 0
500 *** runcommand update -C 0
501 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
501 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
502 (leaving bookmark bm3)
502 (leaving bookmark bm3)
503 *** runcommand commit -Am. a
503 *** runcommand commit -Am. a
504 created new head
504 created new head
505 *** runcommand log -Gq
505 *** runcommand log -Gq
506 @ 5:731265503d86
506 @ 5:731265503d86
507 |
507 |
508 | o 4:7966c8e3734d
508 | o 4:7966c8e3734d
509 | |
509 | |
510 | o 3:b9b85890c400
510 | o 3:b9b85890c400
511 | |
511 | |
512 | o 2:aef17e88f5f0
512 | o 2:aef17e88f5f0
513 | |
513 | |
514 | o 1:d3a0a68be6de
514 | o 1:d3a0a68be6de
515 |/
515 |/
516 o 0:eff892de26ec
516 o 0:eff892de26ec
517
517
518 *** runcommand phase -p .
518 *** runcommand phase -p .
519 *** runcommand phase .
519 *** runcommand phase .
520 5: public
520 5: public
521 *** runcommand branches
521 *** runcommand branches
522 default 1:731265503d86
522 default 1:731265503d86
523
523
524 in-memory cache must be reloaded if transaction is aborted. otherwise
524 in-memory cache must be reloaded if transaction is aborted. otherwise
525 changelog and manifest would have invalid node:
525 changelog and manifest would have invalid node:
526
526
527 $ echo a >> a
527 $ echo a >> a
528 >>> from hgclient import check, readchannel, runcommand
528 >>> from hgclient import check, readchannel, runcommand
529 >>> @check
529 >>> @check
530 ... def txabort(server):
530 ... def txabort(server):
531 ... readchannel(server)
531 ... readchannel(server)
532 ... runcommand(server, [b'commit', b'--config', b'hooks.pretxncommit=false',
532 ... runcommand(server, [b'commit', b'--config', b'hooks.pretxncommit=false',
533 ... b'-mfoo'])
533 ... b'-mfoo'])
534 ... runcommand(server, [b'verify'])
534 ... runcommand(server, [b'verify'])
535 *** runcommand commit --config hooks.pretxncommit=false -mfoo
535 *** runcommand commit --config hooks.pretxncommit=false -mfoo
536 transaction abort!
536 transaction abort!
537 rollback completed
537 rollback completed
538 abort: pretxncommit hook exited with status 1
538 abort: pretxncommit hook exited with status 1
539 [40]
539 [40]
540 *** runcommand verify
540 *** runcommand verify
541 checking changesets
541 checking changesets
542 checking manifests
542 checking manifests
543 crosschecking files in changesets and manifests
543 crosschecking files in changesets and manifests
544 checking files
544 checking files
545 checked 2 changesets with 2 changes to 1 files
545 checked 2 changesets with 2 changes to 1 files
546 $ hg revert --no-backup -aq
546 $ hg revert --no-backup -aq
547
547
548 $ cat >> .hg/hgrc << EOF
548 $ cat >> .hg/hgrc << EOF
549 > [experimental]
549 > [experimental]
550 > evolution.createmarkers=True
550 > evolution.createmarkers=True
551 > EOF
551 > EOF
552
552
553 >>> import os
553 >>> import os
554 >>> from hgclient import check, readchannel, runcommand
554 >>> from hgclient import check, readchannel, runcommand
555 >>> @check
555 >>> @check
556 ... def obsolete(server):
556 ... def obsolete(server):
557 ... readchannel(server)
557 ... readchannel(server)
558 ...
558 ...
559 ... runcommand(server, [b'up', b'null'])
559 ... runcommand(server, [b'up', b'null'])
560 ... runcommand(server, [b'phase', b'-df', b'tip'])
560 ... runcommand(server, [b'phase', b'-df', b'tip'])
561 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
561 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
562 ... if os.name == 'nt':
562 ... if os.name == 'nt':
563 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
563 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
564 ... os.system(cmd)
564 ... os.system(cmd)
565 ... runcommand(server, [b'log', b'--hidden'])
565 ... runcommand(server, [b'log', b'--hidden'])
566 ... runcommand(server, [b'log'])
566 ... runcommand(server, [b'log'])
567 *** runcommand up null
567 *** runcommand up null
568 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
568 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
569 *** runcommand phase -df tip
569 *** runcommand phase -df tip
570 1 new obsolescence markers
570 1 new obsolescence markers
571 obsoleted 1 changesets
571 obsoleted 1 changesets
572 *** runcommand log --hidden
572 *** runcommand log --hidden
573 changeset: 1:731265503d86
573 changeset: 1:731265503d86
574 tag: tip
574 tag: tip
575 user: test
575 user: test
576 date: Thu Jan 01 00:00:00 1970 +0000
576 date: Thu Jan 01 00:00:00 1970 +0000
577 obsolete: pruned
577 obsolete: pruned
578 summary: .
578 summary: .
579
579
580 changeset: 0:eff892de26ec
580 changeset: 0:eff892de26ec
581 bookmark: bm1
581 bookmark: bm1
582 bookmark: bm2
582 bookmark: bm2
583 bookmark: bm3
583 bookmark: bm3
584 user: test
584 user: test
585 date: Thu Jan 01 00:00:00 1970 +0000
585 date: Thu Jan 01 00:00:00 1970 +0000
586 summary: 1
586 summary: 1
587
587
588 *** runcommand log
588 *** runcommand log
589 changeset: 0:eff892de26ec
589 changeset: 0:eff892de26ec
590 bookmark: bm1
590 bookmark: bm1
591 bookmark: bm2
591 bookmark: bm2
592 bookmark: bm3
592 bookmark: bm3
593 tag: tip
593 tag: tip
594 user: test
594 user: test
595 date: Thu Jan 01 00:00:00 1970 +0000
595 date: Thu Jan 01 00:00:00 1970 +0000
596 summary: 1
596 summary: 1
597
597
598
598
599 $ cat <<EOF >> .hg/hgrc
599 $ cat <<EOF >> .hg/hgrc
600 > [extensions]
600 > [extensions]
601 > mq =
601 > mq =
602 > EOF
602 > EOF
603
603
604 >>> import os
604 >>> import os
605 >>> from hgclient import check, readchannel, runcommand
605 >>> from hgclient import check, readchannel, runcommand
606 >>> @check
606 >>> @check
607 ... def mqoutsidechanges(server):
607 ... def mqoutsidechanges(server):
608 ... readchannel(server)
608 ... readchannel(server)
609 ...
609 ...
610 ... # load repo.mq
610 ... # load repo.mq
611 ... runcommand(server, [b'qapplied'])
611 ... runcommand(server, [b'qapplied'])
612 ... os.system('hg qnew 0.diff')
612 ... os.system('hg qnew 0.diff')
613 ... # repo.mq should be invalidated
613 ... # repo.mq should be invalidated
614 ... runcommand(server, [b'qapplied'])
614 ... runcommand(server, [b'qapplied'])
615 ...
615 ...
616 ... runcommand(server, [b'qpop', b'--all'])
616 ... runcommand(server, [b'qpop', b'--all'])
617 ... os.system('hg qqueue --create foo')
617 ... os.system('hg qqueue --create foo')
618 ... # repo.mq should be recreated to point to new queue
618 ... # repo.mq should be recreated to point to new queue
619 ... runcommand(server, [b'qqueue', b'--active'])
619 ... runcommand(server, [b'qqueue', b'--active'])
620 *** runcommand qapplied
620 *** runcommand qapplied
621 *** runcommand qapplied
621 *** runcommand qapplied
622 0.diff
622 0.diff
623 *** runcommand qpop --all
623 *** runcommand qpop --all
624 popping 0.diff
624 popping 0.diff
625 patch queue now empty
625 patch queue now empty
626 *** runcommand qqueue --active
626 *** runcommand qqueue --active
627 foo
627 foo
628
628
629 $ cat <<'EOF' > ../dbgui.py
629 $ cat <<'EOF' > ../dbgui.py
630 > import os
630 > import os
631 > import sys
631 > import sys
632 > from mercurial import commands, registrar
632 > from mercurial import commands, registrar
633 > cmdtable = {}
633 > cmdtable = {}
634 > command = registrar.command(cmdtable)
634 > command = registrar.command(cmdtable)
635 > @command(b"debuggetpass", norepo=True)
635 > @command(b"debuggetpass", norepo=True)
636 > def debuggetpass(ui):
636 > def debuggetpass(ui):
637 > ui.write(b"%s\n" % ui.getpass())
637 > ui.write(b"%s\n" % ui.getpass())
638 > @command(b"debugprompt", norepo=True)
638 > @command(b"debugprompt", norepo=True)
639 > def debugprompt(ui):
639 > def debugprompt(ui):
640 > ui.write(b"%s\n" % ui.prompt(b"prompt:"))
640 > ui.write(b"%s\n" % ui.prompt(b"prompt:"))
641 > @command(b"debugpromptchoice", norepo=True)
641 > @command(b"debugpromptchoice", norepo=True)
642 > def debugpromptchoice(ui):
642 > def debugpromptchoice(ui):
643 > msg = b"promptchoice (y/n)? $$ &Yes $$ &No"
643 > msg = b"promptchoice (y/n)? $$ &Yes $$ &No"
644 > ui.write(b"%d\n" % ui.promptchoice(msg))
644 > ui.write(b"%d\n" % ui.promptchoice(msg))
645 > @command(b"debugreadstdin", norepo=True)
645 > @command(b"debugreadstdin", norepo=True)
646 > def debugreadstdin(ui):
646 > def debugreadstdin(ui):
647 > ui.write(b"read: %r\n" % sys.stdin.read(1))
647 > ui.write(b"read: %r\n" % sys.stdin.read(1))
648 > @command(b"debugwritestdout", norepo=True)
648 > @command(b"debugwritestdout", norepo=True)
649 > def debugwritestdout(ui):
649 > def debugwritestdout(ui):
650 > os.write(1, b"low-level stdout fd and\n")
650 > os.write(1, b"low-level stdout fd and\n")
651 > sys.stdout.write("stdout should be redirected to stderr\n")
651 > sys.stdout.write("stdout should be redirected to stderr\n")
652 > sys.stdout.flush()
652 > sys.stdout.flush()
653 > EOF
653 > EOF
654 $ cat <<EOF >> .hg/hgrc
654 $ cat <<EOF >> .hg/hgrc
655 > [extensions]
655 > [extensions]
656 > dbgui = ../dbgui.py
656 > dbgui = ../dbgui.py
657 > EOF
657 > EOF
658
658
659 >>> from hgclient import check, readchannel, runcommand, stringio
659 >>> from hgclient import check, readchannel, runcommand, stringio
660 >>> @check
660 >>> @check
661 ... def getpass(server):
661 ... def getpass(server):
662 ... readchannel(server)
662 ... readchannel(server)
663 ... runcommand(server, [b'debuggetpass', b'--config',
663 ... runcommand(server, [b'debuggetpass', b'--config',
664 ... b'ui.interactive=True'],
664 ... b'ui.interactive=True'],
665 ... input=stringio(b'1234\n'))
665 ... input=stringio(b'1234\n'))
666 ... runcommand(server, [b'debuggetpass', b'--config',
666 ... runcommand(server, [b'debuggetpass', b'--config',
667 ... b'ui.interactive=True'],
667 ... b'ui.interactive=True'],
668 ... input=stringio(b'\n'))
668 ... input=stringio(b'\n'))
669 ... runcommand(server, [b'debuggetpass', b'--config',
669 ... runcommand(server, [b'debuggetpass', b'--config',
670 ... b'ui.interactive=True'],
670 ... b'ui.interactive=True'],
671 ... input=stringio(b''))
671 ... input=stringio(b''))
672 ... runcommand(server, [b'debugprompt', b'--config',
672 ... runcommand(server, [b'debugprompt', b'--config',
673 ... b'ui.interactive=True'],
673 ... b'ui.interactive=True'],
674 ... input=stringio(b'5678\n'))
674 ... input=stringio(b'5678\n'))
675 ... runcommand(server, [b'debugprompt', b'--config',
675 ... runcommand(server, [b'debugprompt', b'--config',
676 ... b'ui.interactive=True'],
676 ... b'ui.interactive=True'],
677 ... input=stringio(b'\nremainder\nshould\nnot\nbe\nread\n'))
677 ... input=stringio(b'\nremainder\nshould\nnot\nbe\nread\n'))
678 ... runcommand(server, [b'debugreadstdin'])
678 ... runcommand(server, [b'debugreadstdin'])
679 ... runcommand(server, [b'debugwritestdout'])
679 ... runcommand(server, [b'debugwritestdout'])
680 *** runcommand debuggetpass --config ui.interactive=True
680 *** runcommand debuggetpass --config ui.interactive=True
681 password: 1234
681 password: 1234
682 *** runcommand debuggetpass --config ui.interactive=True
682 *** runcommand debuggetpass --config ui.interactive=True
683 password:
683 password:
684 *** runcommand debuggetpass --config ui.interactive=True
684 *** runcommand debuggetpass --config ui.interactive=True
685 password: abort: response expected
685 password: abort: response expected
686 [255]
686 [255]
687 *** runcommand debugprompt --config ui.interactive=True
687 *** runcommand debugprompt --config ui.interactive=True
688 prompt: 5678
688 prompt: 5678
689 *** runcommand debugprompt --config ui.interactive=True
689 *** runcommand debugprompt --config ui.interactive=True
690 prompt: y
690 prompt: y
691 *** runcommand debugreadstdin
691 *** runcommand debugreadstdin
692 read: ''
692 read: ''
693 *** runcommand debugwritestdout
693 *** runcommand debugwritestdout
694 low-level stdout fd and
694 low-level stdout fd and
695 stdout should be redirected to stderr
695 stdout should be redirected to stderr
696
696
697
697
698 run commandserver in commandserver, which is silly but should work:
698 run commandserver in commandserver, which is silly but should work:
699
699
700 >>> from hgclient import bprint, check, readchannel, runcommand, stringio
700 >>> from hgclient import bprint, check, readchannel, runcommand, stringio
701 >>> @check
701 >>> @check
702 ... def nested(server):
702 ... def nested(server):
703 ... bprint(b'%c, %r' % readchannel(server))
703 ... bprint(b'%c, %r' % readchannel(server))
704 ... class nestedserver(object):
704 ... class nestedserver(object):
705 ... stdin = stringio(b'getencoding\n')
705 ... stdin = stringio(b'getencoding\n')
706 ... stdout = stringio()
706 ... stdout = stringio()
707 ... runcommand(server, [b'serve', b'--cmdserver', b'pipe'],
707 ... runcommand(server, [b'serve', b'--cmdserver', b'pipe'],
708 ... output=nestedserver.stdout, input=nestedserver.stdin)
708 ... output=nestedserver.stdout, input=nestedserver.stdin)
709 ... nestedserver.stdout.seek(0)
709 ... nestedserver.stdout.seek(0)
710 ... bprint(b'%c, %r' % readchannel(nestedserver)) # hello
710 ... bprint(b'%c, %r' % readchannel(nestedserver)) # hello
711 ... bprint(b'%c, %r' % readchannel(nestedserver)) # getencoding
711 ... bprint(b'%c, %r' % readchannel(nestedserver)) # getencoding
712 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
712 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
713 *** runcommand serve --cmdserver pipe
713 *** runcommand serve --cmdserver pipe
714 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
714 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
715 r, '*' (glob)
715 r, '*' (glob)
716
716
717
717
718 start without repository:
718 start without repository:
719
719
720 $ cd ..
720 $ cd ..
721
721
722 >>> from hgclient import bprint, check, readchannel, runcommand
722 >>> from hgclient import bprint, check, readchannel, runcommand
723 >>> @check
723 >>> @check
724 ... def hellomessage(server):
724 ... def hellomessage(server):
725 ... ch, data = readchannel(server)
725 ... ch, data = readchannel(server)
726 ... bprint(b'%c, %r' % (ch, data))
726 ... bprint(b'%c, %r' % (ch, data))
727 ... # run an arbitrary command to make sure the next thing the server
727 ... # run an arbitrary command to make sure the next thing the server
728 ... # sends isn't part of the hello message
728 ... # sends isn't part of the hello message
729 ... runcommand(server, [b'id'])
729 ... runcommand(server, [b'id'])
730 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
730 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
731 *** runcommand id
731 *** runcommand id
732 abort: there is no Mercurial repository here (.hg not found)
732 abort: there is no Mercurial repository here (.hg not found)
733 [10]
733 [10]
734
734
735 >>> from hgclient import check, readchannel, runcommand
735 >>> from hgclient import check, readchannel, runcommand
736 >>> @check
736 >>> @check
737 ... def startwithoutrepo(server):
737 ... def startwithoutrepo(server):
738 ... readchannel(server)
738 ... readchannel(server)
739 ... runcommand(server, [b'init', b'repo2'])
739 ... runcommand(server, [b'init', b'repo2'])
740 ... runcommand(server, [b'id', b'-R', b'repo2'])
740 ... runcommand(server, [b'id', b'-R', b'repo2'])
741 *** runcommand init repo2
741 *** runcommand init repo2
742 *** runcommand id -R repo2
742 *** runcommand id -R repo2
743 000000000000 tip
743 000000000000 tip
744
744
745
745
746 don't fall back to cwd if invalid -R path is specified (issue4805):
746 don't fall back to cwd if invalid -R path is specified (issue4805):
747
747
748 $ cd repo
748 $ cd repo
749 $ hg serve --cmdserver pipe -R ../nonexistent
749 $ hg serve --cmdserver pipe -R ../nonexistent
750 abort: repository ../nonexistent not found
750 abort: repository ../nonexistent not found
751 [255]
751 [255]
752 $ cd ..
752 $ cd ..
753
753
754
754
755 #if no-windows
755 #if no-windows
756
756
757 option to not shutdown on SIGINT:
757 option to not shutdown on SIGINT:
758
758
759 $ cat <<'EOF' > dbgint.py
759 $ cat <<'EOF' > dbgint.py
760 > import os
760 > import os
761 > import signal
761 > import signal
762 > import time
762 > import time
763 > from mercurial import commands, registrar
763 > from mercurial import commands, registrar
764 > cmdtable = {}
764 > cmdtable = {}
765 > command = registrar.command(cmdtable)
765 > command = registrar.command(cmdtable)
766 > @command(b"debugsleep", norepo=True)
766 > @command(b"debugsleep", norepo=True)
767 > def debugsleep(ui):
767 > def debugsleep(ui):
768 > time.sleep(1)
768 > time.sleep(1)
769 > @command(b"debugsuicide", norepo=True)
769 > @command(b"debugsuicide", norepo=True)
770 > def debugsuicide(ui):
770 > def debugsuicide(ui):
771 > os.kill(os.getpid(), signal.SIGINT)
771 > os.kill(os.getpid(), signal.SIGINT)
772 > time.sleep(1)
772 > time.sleep(1)
773 > EOF
773 > EOF
774
774
775 >>> import signal
775 >>> import signal
776 >>> import time
776 >>> import time
777 >>> from hgclient import checkwith, readchannel, runcommand
777 >>> from hgclient import checkwith, readchannel, runcommand
778 >>> @checkwith(extraargs=[b'--config', b'cmdserver.shutdown-on-interrupt=False',
778 >>> @checkwith(extraargs=[b'--config', b'cmdserver.shutdown-on-interrupt=False',
779 ... b'--config', b'extensions.dbgint=dbgint.py'])
779 ... b'--config', b'extensions.dbgint=dbgint.py'])
780 ... def nointr(server):
780 ... def nointr(server):
781 ... readchannel(server)
781 ... readchannel(server)
782 ... server.send_signal(signal.SIGINT) # server won't be terminated
782 ... server.send_signal(signal.SIGINT) # server won't be terminated
783 ... time.sleep(1)
783 ... time.sleep(1)
784 ... runcommand(server, [b'debugsleep'])
784 ... runcommand(server, [b'debugsleep'])
785 ... server.send_signal(signal.SIGINT) # server won't be terminated
785 ... server.send_signal(signal.SIGINT) # server won't be terminated
786 ... runcommand(server, [b'debugsleep'])
786 ... runcommand(server, [b'debugsleep'])
787 ... runcommand(server, [b'debugsuicide']) # command can be interrupted
787 ... runcommand(server, [b'debugsuicide']) # command can be interrupted
788 ... server.send_signal(signal.SIGTERM) # server will be terminated
788 ... server.send_signal(signal.SIGTERM) # server will be terminated
789 ... time.sleep(1)
789 ... time.sleep(1)
790 *** runcommand debugsleep
790 *** runcommand debugsleep
791 *** runcommand debugsleep
791 *** runcommand debugsleep
792 *** runcommand debugsuicide
792 *** runcommand debugsuicide
793 interrupted!
793 interrupted!
794 killed!
794 killed!
795 [250]
795 [255]
796
796
797 #endif
797 #endif
798
798
799
799
800 structured message channel:
800 structured message channel:
801
801
802 $ cat <<'EOF' >> repo2/.hg/hgrc
802 $ cat <<'EOF' >> repo2/.hg/hgrc
803 > [ui]
803 > [ui]
804 > # server --config should precede repository option
804 > # server --config should precede repository option
805 > message-output = stdio
805 > message-output = stdio
806 > EOF
806 > EOF
807
807
808 >>> from hgclient import bprint, checkwith, readchannel, runcommand
808 >>> from hgclient import bprint, checkwith, readchannel, runcommand
809 >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
809 >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
810 ... b'--config', b'cmdserver.message-encodings=foo cbor'])
810 ... b'--config', b'cmdserver.message-encodings=foo cbor'])
811 ... def verify(server):
811 ... def verify(server):
812 ... _ch, data = readchannel(server)
812 ... _ch, data = readchannel(server)
813 ... bprint(data)
813 ... bprint(data)
814 ... runcommand(server, [b'-R', b'repo2', b'verify'])
814 ... runcommand(server, [b'-R', b'repo2', b'verify'])
815 capabilities: getencoding runcommand
815 capabilities: getencoding runcommand
816 encoding: ascii
816 encoding: ascii
817 message-encoding: cbor
817 message-encoding: cbor
818 pid: * (glob)
818 pid: * (glob)
819 pgid: * (glob) (no-windows !)
819 pgid: * (glob) (no-windows !)
820 *** runcommand -R repo2 verify
820 *** runcommand -R repo2 verify
821 message: '\xa2DdataTchecking changesets\nDtypeFstatus'
821 message: '\xa2DdataTchecking changesets\nDtypeFstatus'
822 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
822 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
823 message: '\xa2DdataSchecking manifests\nDtypeFstatus'
823 message: '\xa2DdataSchecking manifests\nDtypeFstatus'
824 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
824 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
825 message: '\xa2DdataX0crosschecking files in changesets and manifests\nDtypeFstatus'
825 message: '\xa2DdataX0crosschecking files in changesets and manifests\nDtypeFstatus'
826 message: '\xa6Ditem@Cpos\xf6EtopicMcrosscheckingEtotal\xf6DtypeHprogressDunit@'
826 message: '\xa6Ditem@Cpos\xf6EtopicMcrosscheckingEtotal\xf6DtypeHprogressDunit@'
827 message: '\xa2DdataOchecking files\nDtypeFstatus'
827 message: '\xa2DdataOchecking files\nDtypeFstatus'
828 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
828 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
829 message: '\xa2DdataX/checked 0 changesets with 0 changes to 0 files\nDtypeFstatus'
829 message: '\xa2DdataX/checked 0 changesets with 0 changes to 0 files\nDtypeFstatus'
830
830
831 >>> from hgclient import checkwith, readchannel, runcommand, stringio
831 >>> from hgclient import checkwith, readchannel, runcommand, stringio
832 >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
832 >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
833 ... b'--config', b'cmdserver.message-encodings=cbor',
833 ... b'--config', b'cmdserver.message-encodings=cbor',
834 ... b'--config', b'extensions.dbgui=dbgui.py'])
834 ... b'--config', b'extensions.dbgui=dbgui.py'])
835 ... def prompt(server):
835 ... def prompt(server):
836 ... readchannel(server)
836 ... readchannel(server)
837 ... interactive = [b'--config', b'ui.interactive=True']
837 ... interactive = [b'--config', b'ui.interactive=True']
838 ... runcommand(server, [b'debuggetpass'] + interactive,
838 ... runcommand(server, [b'debuggetpass'] + interactive,
839 ... input=stringio(b'1234\n'))
839 ... input=stringio(b'1234\n'))
840 ... runcommand(server, [b'debugprompt'] + interactive,
840 ... runcommand(server, [b'debugprompt'] + interactive,
841 ... input=stringio(b'5678\n'))
841 ... input=stringio(b'5678\n'))
842 ... runcommand(server, [b'debugpromptchoice'] + interactive,
842 ... runcommand(server, [b'debugpromptchoice'] + interactive,
843 ... input=stringio(b'n\n'))
843 ... input=stringio(b'n\n'))
844 *** runcommand debuggetpass --config ui.interactive=True
844 *** runcommand debuggetpass --config ui.interactive=True
845 message: '\xa3DdataJpassword: Hpassword\xf5DtypeFprompt'
845 message: '\xa3DdataJpassword: Hpassword\xf5DtypeFprompt'
846 1234
846 1234
847 *** runcommand debugprompt --config ui.interactive=True
847 *** runcommand debugprompt --config ui.interactive=True
848 message: '\xa3DdataGprompt:GdefaultAyDtypeFprompt'
848 message: '\xa3DdataGprompt:GdefaultAyDtypeFprompt'
849 5678
849 5678
850 *** runcommand debugpromptchoice --config ui.interactive=True
850 *** runcommand debugpromptchoice --config ui.interactive=True
851 message: '\xa4Gchoices\x82\x82AyCYes\x82AnBNoDdataTpromptchoice (y/n)? GdefaultAyDtypeFprompt'
851 message: '\xa4Gchoices\x82\x82AyCYes\x82AnBNoDdataTpromptchoice (y/n)? GdefaultAyDtypeFprompt'
852 1
852 1
853
853
854 bad message encoding:
854 bad message encoding:
855
855
856 $ hg serve --cmdserver pipe --config ui.message-output=channel
856 $ hg serve --cmdserver pipe --config ui.message-output=channel
857 abort: no supported message encodings:
857 abort: no supported message encodings:
858 [255]
858 [255]
859 $ hg serve --cmdserver pipe --config ui.message-output=channel \
859 $ hg serve --cmdserver pipe --config ui.message-output=channel \
860 > --config cmdserver.message-encodings='foo bar'
860 > --config cmdserver.message-encodings='foo bar'
861 abort: no supported message encodings: foo bar
861 abort: no supported message encodings: foo bar
862 [255]
862 [255]
863
863
864 unix domain socket:
864 unix domain socket:
865
865
866 $ cd repo
866 $ cd repo
867 $ hg update -q
867 $ hg update -q
868
868
869 #if unix-socket unix-permissions
869 #if unix-socket unix-permissions
870
870
871 >>> from hgclient import bprint, check, readchannel, runcommand, stringio, unixserver
871 >>> from hgclient import bprint, check, readchannel, runcommand, stringio, unixserver
872 >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
872 >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
873 >>> def hellomessage(conn):
873 >>> def hellomessage(conn):
874 ... ch, data = readchannel(conn)
874 ... ch, data = readchannel(conn)
875 ... bprint(b'%c, %r' % (ch, data))
875 ... bprint(b'%c, %r' % (ch, data))
876 ... runcommand(conn, [b'id'])
876 ... runcommand(conn, [b'id'])
877 >>> check(hellomessage, server.connect)
877 >>> check(hellomessage, server.connect)
878 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
878 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
879 *** runcommand id
879 *** runcommand id
880 eff892de26ec tip bm1/bm2/bm3
880 eff892de26ec tip bm1/bm2/bm3
881 >>> def unknowncommand(conn):
881 >>> def unknowncommand(conn):
882 ... readchannel(conn)
882 ... readchannel(conn)
883 ... conn.stdin.write(b'unknowncommand\n')
883 ... conn.stdin.write(b'unknowncommand\n')
884 >>> check(unknowncommand, server.connect) # error sent to server.log
884 >>> check(unknowncommand, server.connect) # error sent to server.log
885 >>> def serverinput(conn):
885 >>> def serverinput(conn):
886 ... readchannel(conn)
886 ... readchannel(conn)
887 ... patch = b"""
887 ... patch = b"""
888 ... # HG changeset patch
888 ... # HG changeset patch
889 ... # User test
889 ... # User test
890 ... # Date 0 0
890 ... # Date 0 0
891 ... 2
891 ... 2
892 ...
892 ...
893 ... diff -r eff892de26ec -r 1ed24be7e7a0 a
893 ... diff -r eff892de26ec -r 1ed24be7e7a0 a
894 ... --- a/a
894 ... --- a/a
895 ... +++ b/a
895 ... +++ b/a
896 ... @@ -1,1 +1,2 @@
896 ... @@ -1,1 +1,2 @@
897 ... 1
897 ... 1
898 ... +2
898 ... +2
899 ... """
899 ... """
900 ... runcommand(conn, [b'import', b'-'], input=stringio(patch))
900 ... runcommand(conn, [b'import', b'-'], input=stringio(patch))
901 ... runcommand(conn, [b'log', b'-rtip', b'-q'])
901 ... runcommand(conn, [b'log', b'-rtip', b'-q'])
902 >>> check(serverinput, server.connect)
902 >>> check(serverinput, server.connect)
903 *** runcommand import -
903 *** runcommand import -
904 applying patch from stdin
904 applying patch from stdin
905 *** runcommand log -rtip -q
905 *** runcommand log -rtip -q
906 2:1ed24be7e7a0
906 2:1ed24be7e7a0
907 >>> server.shutdown()
907 >>> server.shutdown()
908
908
909 $ cat .hg/server.log
909 $ cat .hg/server.log
910 listening at .hg/server.sock
910 listening at .hg/server.sock
911 abort: unknown command unknowncommand
911 abort: unknown command unknowncommand
912 killed!
912 killed!
913 $ rm .hg/server.log
913 $ rm .hg/server.log
914
914
915 if server crashed before hello, traceback will be sent to 'e' channel as
915 if server crashed before hello, traceback will be sent to 'e' channel as
916 last ditch:
916 last ditch:
917
917
918 $ cat <<'EOF' > ../earlycrasher.py
918 $ cat <<'EOF' > ../earlycrasher.py
919 > from mercurial import commandserver, extensions
919 > from mercurial import commandserver, extensions
920 > def _serverequest(orig, ui, repo, conn, createcmdserver, prereposetups):
920 > def _serverequest(orig, ui, repo, conn, createcmdserver, prereposetups):
921 > def createcmdserver(*args, **kwargs):
921 > def createcmdserver(*args, **kwargs):
922 > raise Exception('crash')
922 > raise Exception('crash')
923 > return orig(ui, repo, conn, createcmdserver, prereposetups)
923 > return orig(ui, repo, conn, createcmdserver, prereposetups)
924 > def extsetup(ui):
924 > def extsetup(ui):
925 > extensions.wrapfunction(commandserver, b'_serverequest', _serverequest)
925 > extensions.wrapfunction(commandserver, b'_serverequest', _serverequest)
926 > EOF
926 > EOF
927 $ cat <<EOF >> .hg/hgrc
927 $ cat <<EOF >> .hg/hgrc
928 > [extensions]
928 > [extensions]
929 > earlycrasher = ../earlycrasher.py
929 > earlycrasher = ../earlycrasher.py
930 > EOF
930 > EOF
931 >>> from hgclient import bprint, check, readchannel, unixserver
931 >>> from hgclient import bprint, check, readchannel, unixserver
932 >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
932 >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
933 >>> def earlycrash(conn):
933 >>> def earlycrash(conn):
934 ... while True:
934 ... while True:
935 ... try:
935 ... try:
936 ... ch, data = readchannel(conn)
936 ... ch, data = readchannel(conn)
937 ... for l in data.splitlines(True):
937 ... for l in data.splitlines(True):
938 ... if not l.startswith(b' '):
938 ... if not l.startswith(b' '):
939 ... bprint(b'%c, %r' % (ch, l))
939 ... bprint(b'%c, %r' % (ch, l))
940 ... except EOFError:
940 ... except EOFError:
941 ... break
941 ... break
942 >>> check(earlycrash, server.connect)
942 >>> check(earlycrash, server.connect)
943 e, 'Traceback (most recent call last):\n'
943 e, 'Traceback (most recent call last):\n'
944 e, 'Exception: crash\n'
944 e, 'Exception: crash\n'
945 >>> server.shutdown()
945 >>> server.shutdown()
946
946
947 $ cat .hg/server.log | grep -v '^ '
947 $ cat .hg/server.log | grep -v '^ '
948 listening at .hg/server.sock
948 listening at .hg/server.sock
949 Traceback (most recent call last):
949 Traceback (most recent call last):
950 Exception: crash
950 Exception: crash
951 killed!
951 killed!
952 #endif
952 #endif
953 #if no-unix-socket
953 #if no-unix-socket
954
954
955 $ hg serve --cmdserver unix -a .hg/server.sock
955 $ hg serve --cmdserver unix -a .hg/server.sock
956 abort: unsupported platform
956 abort: unsupported platform
957 [255]
957 [255]
958
958
959 #endif
959 #endif
960
960
961 $ cd ..
961 $ cd ..
962
962
963 Test that accessing to invalid changelog cache is avoided at
963 Test that accessing to invalid changelog cache is avoided at
964 subsequent operations even if repo object is reused even after failure
964 subsequent operations even if repo object is reused even after failure
965 of transaction (see 0a7610758c42 also)
965 of transaction (see 0a7610758c42 also)
966
966
967 "hg log" after failure of transaction is needed to detect invalid
967 "hg log" after failure of transaction is needed to detect invalid
968 cache in repoview: this can't detect by "hg verify" only.
968 cache in repoview: this can't detect by "hg verify" only.
969
969
970 Combination of "finalization" and "empty-ness of changelog" (2 x 2 =
970 Combination of "finalization" and "empty-ness of changelog" (2 x 2 =
971 4) are tested, because '00changelog.i' are differently changed in each
971 4) are tested, because '00changelog.i' are differently changed in each
972 cases.
972 cases.
973
973
974 $ cat > $TESTTMP/failafterfinalize.py <<EOF
974 $ cat > $TESTTMP/failafterfinalize.py <<EOF
975 > # extension to abort transaction after finalization forcibly
975 > # extension to abort transaction after finalization forcibly
976 > from mercurial import commands, error, extensions, lock as lockmod
976 > from mercurial import commands, error, extensions, lock as lockmod
977 > from mercurial import registrar
977 > from mercurial import registrar
978 > cmdtable = {}
978 > cmdtable = {}
979 > command = registrar.command(cmdtable)
979 > command = registrar.command(cmdtable)
980 > configtable = {}
980 > configtable = {}
981 > configitem = registrar.configitem(configtable)
981 > configitem = registrar.configitem(configtable)
982 > configitem(b'failafterfinalize', b'fail',
982 > configitem(b'failafterfinalize', b'fail',
983 > default=None,
983 > default=None,
984 > )
984 > )
985 > def fail(tr):
985 > def fail(tr):
986 > raise error.Abort(b'fail after finalization')
986 > raise error.Abort(b'fail after finalization')
987 > def reposetup(ui, repo):
987 > def reposetup(ui, repo):
988 > class failrepo(repo.__class__):
988 > class failrepo(repo.__class__):
989 > def commitctx(self, ctx, error=False, origctx=None):
989 > def commitctx(self, ctx, error=False, origctx=None):
990 > if self.ui.configbool(b'failafterfinalize', b'fail'):
990 > if self.ui.configbool(b'failafterfinalize', b'fail'):
991 > # 'sorted()' by ASCII code on category names causes
991 > # 'sorted()' by ASCII code on category names causes
992 > # invoking 'fail' after finalization of changelog
992 > # invoking 'fail' after finalization of changelog
993 > # using "'cl-%i' % id(self)" as category name
993 > # using "'cl-%i' % id(self)" as category name
994 > self.currenttransaction().addfinalize(b'zzzzzzzz', fail)
994 > self.currenttransaction().addfinalize(b'zzzzzzzz', fail)
995 > return super(failrepo, self).commitctx(ctx, error, origctx)
995 > return super(failrepo, self).commitctx(ctx, error, origctx)
996 > repo.__class__ = failrepo
996 > repo.__class__ = failrepo
997 > EOF
997 > EOF
998
998
999 $ hg init repo3
999 $ hg init repo3
1000 $ cd repo3
1000 $ cd repo3
1001
1001
1002 $ cat <<EOF >> $HGRCPATH
1002 $ cat <<EOF >> $HGRCPATH
1003 > [command-templates]
1003 > [command-templates]
1004 > log = {rev} {desc|firstline} ({files})\n
1004 > log = {rev} {desc|firstline} ({files})\n
1005 >
1005 >
1006 > [extensions]
1006 > [extensions]
1007 > failafterfinalize = $TESTTMP/failafterfinalize.py
1007 > failafterfinalize = $TESTTMP/failafterfinalize.py
1008 > EOF
1008 > EOF
1009
1009
1010 - test failure with "empty changelog"
1010 - test failure with "empty changelog"
1011
1011
1012 $ echo foo > foo
1012 $ echo foo > foo
1013 $ hg add foo
1013 $ hg add foo
1014
1014
1015 (failure before finalization)
1015 (failure before finalization)
1016
1016
1017 >>> from hgclient import check, readchannel, runcommand
1017 >>> from hgclient import check, readchannel, runcommand
1018 >>> @check
1018 >>> @check
1019 ... def abort(server):
1019 ... def abort(server):
1020 ... readchannel(server)
1020 ... readchannel(server)
1021 ... runcommand(server, [b'commit',
1021 ... runcommand(server, [b'commit',
1022 ... b'--config', b'hooks.pretxncommit=false',
1022 ... b'--config', b'hooks.pretxncommit=false',
1023 ... b'-mfoo'])
1023 ... b'-mfoo'])
1024 ... runcommand(server, [b'log'])
1024 ... runcommand(server, [b'log'])
1025 ... runcommand(server, [b'verify', b'-q'])
1025 ... runcommand(server, [b'verify', b'-q'])
1026 *** runcommand commit --config hooks.pretxncommit=false -mfoo
1026 *** runcommand commit --config hooks.pretxncommit=false -mfoo
1027 transaction abort!
1027 transaction abort!
1028 rollback completed
1028 rollback completed
1029 abort: pretxncommit hook exited with status 1
1029 abort: pretxncommit hook exited with status 1
1030 [40]
1030 [40]
1031 *** runcommand log
1031 *** runcommand log
1032 *** runcommand verify -q
1032 *** runcommand verify -q
1033
1033
1034 (failure after finalization)
1034 (failure after finalization)
1035
1035
1036 >>> from hgclient import check, readchannel, runcommand
1036 >>> from hgclient import check, readchannel, runcommand
1037 >>> @check
1037 >>> @check
1038 ... def abort(server):
1038 ... def abort(server):
1039 ... readchannel(server)
1039 ... readchannel(server)
1040 ... runcommand(server, [b'commit',
1040 ... runcommand(server, [b'commit',
1041 ... b'--config', b'failafterfinalize.fail=true',
1041 ... b'--config', b'failafterfinalize.fail=true',
1042 ... b'-mfoo'])
1042 ... b'-mfoo'])
1043 ... runcommand(server, [b'log'])
1043 ... runcommand(server, [b'log'])
1044 ... runcommand(server, [b'verify', b'-q'])
1044 ... runcommand(server, [b'verify', b'-q'])
1045 *** runcommand commit --config failafterfinalize.fail=true -mfoo
1045 *** runcommand commit --config failafterfinalize.fail=true -mfoo
1046 transaction abort!
1046 transaction abort!
1047 rollback completed
1047 rollback completed
1048 abort: fail after finalization
1048 abort: fail after finalization
1049 [255]
1049 [255]
1050 *** runcommand log
1050 *** runcommand log
1051 *** runcommand verify -q
1051 *** runcommand verify -q
1052
1052
1053 - test failure with "not-empty changelog"
1053 - test failure with "not-empty changelog"
1054
1054
1055 $ echo bar > bar
1055 $ echo bar > bar
1056 $ hg add bar
1056 $ hg add bar
1057 $ hg commit -mbar bar
1057 $ hg commit -mbar bar
1058
1058
1059 (failure before finalization)
1059 (failure before finalization)
1060
1060
1061 >>> from hgclient import check, readchannel, runcommand
1061 >>> from hgclient import check, readchannel, runcommand
1062 >>> @check
1062 >>> @check
1063 ... def abort(server):
1063 ... def abort(server):
1064 ... readchannel(server)
1064 ... readchannel(server)
1065 ... runcommand(server, [b'commit',
1065 ... runcommand(server, [b'commit',
1066 ... b'--config', b'hooks.pretxncommit=false',
1066 ... b'--config', b'hooks.pretxncommit=false',
1067 ... b'-mfoo', b'foo'])
1067 ... b'-mfoo', b'foo'])
1068 ... runcommand(server, [b'log'])
1068 ... runcommand(server, [b'log'])
1069 ... runcommand(server, [b'verify', b'-q'])
1069 ... runcommand(server, [b'verify', b'-q'])
1070 *** runcommand commit --config hooks.pretxncommit=false -mfoo foo
1070 *** runcommand commit --config hooks.pretxncommit=false -mfoo foo
1071 transaction abort!
1071 transaction abort!
1072 rollback completed
1072 rollback completed
1073 abort: pretxncommit hook exited with status 1
1073 abort: pretxncommit hook exited with status 1
1074 [40]
1074 [40]
1075 *** runcommand log
1075 *** runcommand log
1076 0 bar (bar)
1076 0 bar (bar)
1077 *** runcommand verify -q
1077 *** runcommand verify -q
1078
1078
1079 (failure after finalization)
1079 (failure after finalization)
1080
1080
1081 >>> from hgclient import check, readchannel, runcommand
1081 >>> from hgclient import check, readchannel, runcommand
1082 >>> @check
1082 >>> @check
1083 ... def abort(server):
1083 ... def abort(server):
1084 ... readchannel(server)
1084 ... readchannel(server)
1085 ... runcommand(server, [b'commit',
1085 ... runcommand(server, [b'commit',
1086 ... b'--config', b'failafterfinalize.fail=true',
1086 ... b'--config', b'failafterfinalize.fail=true',
1087 ... b'-mfoo', b'foo'])
1087 ... b'-mfoo', b'foo'])
1088 ... runcommand(server, [b'log'])
1088 ... runcommand(server, [b'log'])
1089 ... runcommand(server, [b'verify', b'-q'])
1089 ... runcommand(server, [b'verify', b'-q'])
1090 *** runcommand commit --config failafterfinalize.fail=true -mfoo foo
1090 *** runcommand commit --config failafterfinalize.fail=true -mfoo foo
1091 transaction abort!
1091 transaction abort!
1092 rollback completed
1092 rollback completed
1093 abort: fail after finalization
1093 abort: fail after finalization
1094 [255]
1094 [255]
1095 *** runcommand log
1095 *** runcommand log
1096 0 bar (bar)
1096 0 bar (bar)
1097 *** runcommand verify -q
1097 *** runcommand verify -q
1098
1098
1099 $ cd ..
1099 $ cd ..
1100
1100
1101 Test symlink traversal over cached audited paths:
1101 Test symlink traversal over cached audited paths:
1102 -------------------------------------------------
1102 -------------------------------------------------
1103
1103
1104 #if symlink
1104 #if symlink
1105
1105
1106 set up symlink hell
1106 set up symlink hell
1107
1107
1108 $ mkdir merge-symlink-out
1108 $ mkdir merge-symlink-out
1109 $ hg init merge-symlink
1109 $ hg init merge-symlink
1110 $ cd merge-symlink
1110 $ cd merge-symlink
1111 $ touch base
1111 $ touch base
1112 $ hg commit -qAm base
1112 $ hg commit -qAm base
1113 $ ln -s ../merge-symlink-out a
1113 $ ln -s ../merge-symlink-out a
1114 $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
1114 $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
1115 $ hg up -q 0
1115 $ hg up -q 0
1116 $ mkdir a
1116 $ mkdir a
1117 $ touch a/poisoned
1117 $ touch a/poisoned
1118 $ hg commit -qAm 'file a/poisoned'
1118 $ hg commit -qAm 'file a/poisoned'
1119 $ hg log -G -T '{rev}: {desc}\n'
1119 $ hg log -G -T '{rev}: {desc}\n'
1120 @ 2: file a/poisoned
1120 @ 2: file a/poisoned
1121 |
1121 |
1122 | o 1: symlink a -> ../merge-symlink-out
1122 | o 1: symlink a -> ../merge-symlink-out
1123 |/
1123 |/
1124 o 0: base
1124 o 0: base
1125
1125
1126
1126
1127 try trivial merge after update: cache of audited paths should be discarded,
1127 try trivial merge after update: cache of audited paths should be discarded,
1128 and the merge should fail (issue5628)
1128 and the merge should fail (issue5628)
1129
1129
1130 $ hg up -q null
1130 $ hg up -q null
1131 >>> from hgclient import check, readchannel, runcommand
1131 >>> from hgclient import check, readchannel, runcommand
1132 >>> @check
1132 >>> @check
1133 ... def merge(server):
1133 ... def merge(server):
1134 ... readchannel(server)
1134 ... readchannel(server)
1135 ... # audit a/poisoned as a good path
1135 ... # audit a/poisoned as a good path
1136 ... runcommand(server, [b'up', b'-qC', b'2'])
1136 ... runcommand(server, [b'up', b'-qC', b'2'])
1137 ... runcommand(server, [b'up', b'-qC', b'1'])
1137 ... runcommand(server, [b'up', b'-qC', b'1'])
1138 ... # here a is a symlink, so a/poisoned is bad
1138 ... # here a is a symlink, so a/poisoned is bad
1139 ... runcommand(server, [b'merge', b'2'])
1139 ... runcommand(server, [b'merge', b'2'])
1140 *** runcommand up -qC 2
1140 *** runcommand up -qC 2
1141 *** runcommand up -qC 1
1141 *** runcommand up -qC 1
1142 *** runcommand merge 2
1142 *** runcommand merge 2
1143 abort: path 'a/poisoned' traverses symbolic link 'a'
1143 abort: path 'a/poisoned' traverses symbolic link 'a'
1144 [255]
1144 [255]
1145 $ ls ../merge-symlink-out
1145 $ ls ../merge-symlink-out
1146
1146
1147 cache of repo.auditor should be discarded, so matcher would never traverse
1147 cache of repo.auditor should be discarded, so matcher would never traverse
1148 symlinks:
1148 symlinks:
1149
1149
1150 $ hg up -qC 0
1150 $ hg up -qC 0
1151 $ touch ../merge-symlink-out/poisoned
1151 $ touch ../merge-symlink-out/poisoned
1152 >>> from hgclient import check, readchannel, runcommand
1152 >>> from hgclient import check, readchannel, runcommand
1153 >>> @check
1153 >>> @check
1154 ... def files(server):
1154 ... def files(server):
1155 ... readchannel(server)
1155 ... readchannel(server)
1156 ... runcommand(server, [b'up', b'-qC', b'2'])
1156 ... runcommand(server, [b'up', b'-qC', b'2'])
1157 ... # audit a/poisoned as a good path
1157 ... # audit a/poisoned as a good path
1158 ... runcommand(server, [b'files', b'a/poisoned'])
1158 ... runcommand(server, [b'files', b'a/poisoned'])
1159 ... runcommand(server, [b'up', b'-qC', b'0'])
1159 ... runcommand(server, [b'up', b'-qC', b'0'])
1160 ... runcommand(server, [b'up', b'-qC', b'1'])
1160 ... runcommand(server, [b'up', b'-qC', b'1'])
1161 ... # here 'a' is a symlink, so a/poisoned should be warned
1161 ... # here 'a' is a symlink, so a/poisoned should be warned
1162 ... runcommand(server, [b'files', b'a/poisoned'])
1162 ... runcommand(server, [b'files', b'a/poisoned'])
1163 *** runcommand up -qC 2
1163 *** runcommand up -qC 2
1164 *** runcommand files a/poisoned
1164 *** runcommand files a/poisoned
1165 a/poisoned
1165 a/poisoned
1166 *** runcommand up -qC 0
1166 *** runcommand up -qC 0
1167 *** runcommand up -qC 1
1167 *** runcommand up -qC 1
1168 *** runcommand files a/poisoned
1168 *** runcommand files a/poisoned
1169 abort: path 'a/poisoned' traverses symbolic link 'a'
1169 abort: path 'a/poisoned' traverses symbolic link 'a'
1170 [255]
1170 [255]
1171
1171
1172 $ cd ..
1172 $ cd ..
1173
1173
1174 #endif
1174 #endif
@@ -1,94 +1,94 b''
1 #require no-windows no-rhg
1 #require no-windows no-rhg
2
2
3 XXX-RHG this test hangs if `hg` is really `rhg`. This was hidden by the use of
3 XXX-RHG this test hangs if `hg` is really `rhg`. This was hidden by the use of
4 `alias hg=rhg` by run-tests.py. With such alias removed, this test is revealed
4 `alias hg=rhg` by run-tests.py. With such alias removed, this test is revealed
5 buggy. This need to be resolved sooner than later.
5 buggy. This need to be resolved sooner than later.
6
6
7 Dummy extension simulating unsafe long running command
7 Dummy extension simulating unsafe long running command
8 $ SYNC_FILE="$TESTTMP/sync-file"
8 $ SYNC_FILE="$TESTTMP/sync-file"
9 $ export SYNC_FILE
9 $ export SYNC_FILE
10 $ DONE_FILE="$TESTTMP/done-file"
10 $ DONE_FILE="$TESTTMP/done-file"
11 $ export DONE_FILE
11 $ export DONE_FILE
12 $
12 $
13 $ cat > wait_ext.py <<EOF
13 $ cat > wait_ext.py <<EOF
14 > import os
14 > import os
15 > import time
15 > import time
16 >
16 >
17 > from mercurial.i18n import _
17 > from mercurial.i18n import _
18 > from mercurial import registrar
18 > from mercurial import registrar
19 > from mercurial import testing
19 > from mercurial import testing
20 >
20 >
21 > cmdtable = {}
21 > cmdtable = {}
22 > command = registrar.command(cmdtable)
22 > command = registrar.command(cmdtable)
23 >
23 >
24 > @command(b'wait-signal', [], _(b'SYNC_FILE DONE_FILE'), norepo=True)
24 > @command(b'wait-signal', [], _(b'SYNC_FILE DONE_FILE'), norepo=True)
25 > def sleep(ui, sync_file=b"$SYNC_FILE", done_file=b"$DONE_FILE", **opts):
25 > def sleep(ui, sync_file=b"$SYNC_FILE", done_file=b"$DONE_FILE", **opts):
26 > start = time.time()
26 > start = time.time()
27 > with ui.uninterruptible():
27 > with ui.uninterruptible():
28 > testing.write_file(sync_file, b'%d' % os.getpid())
28 > testing.write_file(sync_file, b'%d' % os.getpid())
29 > testing.wait_file(done_file)
29 > testing.wait_file(done_file)
30 > # make sure we get rescheduled and the signal get a chance to be handled
30 > # make sure we get rescheduled and the signal get a chance to be handled
31 > time.sleep(0.1)
31 > time.sleep(0.1)
32 > ui.warn(b"end of unsafe operation\n")
32 > ui.warn(b"end of unsafe operation\n")
33 > ui.warn(b"%d second(s) passed\n" % int(time.time() - start))
33 > ui.warn(b"%d second(s) passed\n" % int(time.time() - start))
34 > EOF
34 > EOF
35
35
36 $ cat > send-signal.sh << EOF
36 $ cat > send-signal.sh << EOF
37 > #!/bin/sh
37 > #!/bin/sh
38 > SIG=\$1
38 > SIG=\$1
39 > if [ -z "\$SIG" ]; then
39 > if [ -z "\$SIG" ]; then
40 > echo "send-signal.sh requires one argument" >&2
40 > echo "send-signal.sh requires one argument" >&2
41 > exit 1
41 > exit 1
42 > fi
42 > fi
43 > "$RUNTESTDIR/testlib/wait-on-file" 10 "$SYNC_FILE" || exit 2
43 > "$RUNTESTDIR/testlib/wait-on-file" 10 "$SYNC_FILE" || exit 2
44 > kill -s \$SIG \`cat "$SYNC_FILE"\`
44 > kill -s \$SIG \`cat "$SYNC_FILE"\`
45 > sleep 1
45 > sleep 1
46 > touch "$DONE_FILE"
46 > touch "$DONE_FILE"
47 > EOF
47 > EOF
48
48
49 #if no-windows
49 #if no-windows
50 $ chmod +x send-signal.sh
50 $ chmod +x send-signal.sh
51 #endif
51 #endif
52
52
53 Kludge to emulate timeout(1) which is not generally available.
53 Kludge to emulate timeout(1) which is not generally available.
54
54
55 Set up repository
55 Set up repository
56 $ hg init repo
56 $ hg init repo
57 $ cd repo
57 $ cd repo
58 $ cat >> $HGRCPATH << EOF
58 $ cat >> $HGRCPATH << EOF
59 > [extensions]
59 > [extensions]
60 > wait_ext = $TESTTMP/wait_ext.py
60 > wait_ext = $TESTTMP/wait_ext.py
61 > EOF
61 > EOF
62
62
63
63
64 Test ctrl-c
64 Test ctrl-c
65 $ rm -f $SYNC_FILE $DONE_FILE
65 $ rm -f $SYNC_FILE $DONE_FILE
66 $ sh -c "../send-signal.sh INT" &
66 $ sh -c "../send-signal.sh INT" &
67 $ hg wait-signal
67 $ hg wait-signal
68 interrupted!
68 interrupted!
69 [250]
69 [255]
70
70
71 $ cat >> $HGRCPATH << EOF
71 $ cat >> $HGRCPATH << EOF
72 > [experimental]
72 > [experimental]
73 > nointerrupt = yes
73 > nointerrupt = yes
74 > EOF
74 > EOF
75
75
76 $ rm -f $SYNC_FILE $DONE_FILE
76 $ rm -f $SYNC_FILE $DONE_FILE
77 $ sh -c "../send-signal.sh INT" &
77 $ sh -c "../send-signal.sh INT" &
78 $ hg wait-signal
78 $ hg wait-signal
79 interrupted!
79 interrupted!
80 [250]
80 [255]
81
81
82 $ cat >> $HGRCPATH << EOF
82 $ cat >> $HGRCPATH << EOF
83 > [experimental]
83 > [experimental]
84 > nointerrupt-interactiveonly = False
84 > nointerrupt-interactiveonly = False
85 > EOF
85 > EOF
86
86
87 $ rm -f $SYNC_FILE $DONE_FILE
87 $ rm -f $SYNC_FILE $DONE_FILE
88 $ sh -c "../send-signal.sh INT" &
88 $ sh -c "../send-signal.sh INT" &
89 $ hg wait-signal
89 $ hg wait-signal
90 shutting down cleanly
90 shutting down cleanly
91 press ^C again to terminate immediately (dangerous)
91 press ^C again to terminate immediately (dangerous)
92 end of unsafe operation
92 end of unsafe operation
93 interrupted!
93 interrupted!
94 [250]
94 [255]
General Comments 0
You need to be logged in to leave comments. Login now