##// END OF EJS Templates
commandserver: preload repository in master server and reuse its file cache...
Yuya Nishihara -
r41035:dcac24ec default
parent child Browse files
Show More
@@ -0,0 +1,131 b''
1 # repocache.py - in-memory repository cache for long-running services
2 #
3 # Copyright 2018 Yuya Nishihara <yuya@tcha.org>
4 #
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.
7
8 from __future__ import absolute_import
9
10 import collections
11 import gc
12 import threading
13
14 from . import (
15 error,
16 hg,
17 obsolete,
18 scmutil,
19 util,
20 )
21
22 class repoloader(object):
23 """Load repositories in background thread
24
25 This is designed for a forking server. A cached repo cannot be obtained
26 until the server fork()s a worker and the loader thread stops.
27 """
28
29 def __init__(self, ui, maxlen):
30 self._ui = ui.copy()
31 self._cache = util.lrucachedict(max=maxlen)
32 # use deque and Event instead of Queue since deque can discard
33 # old items to keep at most maxlen items.
34 self._inqueue = collections.deque(maxlen=maxlen)
35 self._accepting = False
36 self._newentry = threading.Event()
37 self._thread = None
38
39 def start(self):
40 assert not self._thread
41 if self._inqueue.maxlen == 0:
42 # no need to spawn loader thread as the cache is disabled
43 return
44 self._accepting = True
45 self._thread = threading.Thread(target=self._mainloop)
46 self._thread.start()
47
48 def stop(self):
49 if not self._thread:
50 return
51 self._accepting = False
52 self._newentry.set()
53 self._thread.join()
54 self._thread = None
55 self._cache.clear()
56 self._inqueue.clear()
57
58 def load(self, path):
59 """Request to load the specified repository in background"""
60 self._inqueue.append(path)
61 self._newentry.set()
62
63 def get(self, path):
64 """Return a cached repo if available
65
66 This function must be called after fork(), where the loader thread
67 is stopped. Otherwise, the returned repo might be updated by the
68 loader thread.
69 """
70 if self._thread and self._thread.is_alive():
71 raise error.ProgrammingError(b'cannot obtain cached repo while '
72 b'loader is active')
73 return self._cache.peek(path, None)
74
75 def _mainloop(self):
76 while self._accepting:
77 # Avoid heavy GC after fork(), which would cancel the benefit of
78 # COW. We assume that GIL is acquired while GC is underway in the
79 # loader thread. If that isn't true, we might have to move
80 # gc.collect() to the main thread so that fork() would never stop
81 # the thread where GC is in progress.
82 gc.collect()
83
84 self._newentry.wait()
85 while self._accepting:
86 self._newentry.clear()
87 try:
88 path = self._inqueue.popleft()
89 except IndexError:
90 break
91 scmutil.callcatch(self._ui, lambda: self._load(path))
92
93 def _load(self, path):
94 start = util.timer()
95 # TODO: repo should be recreated if storage configuration changed
96 try:
97 # pop before loading so inconsistent state wouldn't be exposed
98 repo = self._cache.pop(path)
99 except KeyError:
100 repo = hg.repository(self._ui, path).unfiltered()
101 _warmupcache(repo)
102 repo.ui.log(b'repocache', b'loaded repo into cache: %s (in %.3fs)\n',
103 path, util.timer() - start)
104 self._cache.insert(path, repo)
105
106 # TODO: think about proper API of preloading cache
107 def _warmupcache(repo):
108 repo.invalidateall()
109 repo.changelog
110 repo.obsstore._all
111 repo.obsstore.successors
112 repo.obsstore.predecessors
113 repo.obsstore.children
114 for name in obsolete.cachefuncs:
115 obsolete.getrevs(repo, name)
116 repo._phasecache.loadphaserevs(repo)
117
118 # TODO: think about proper API of attaching preloaded attributes
119 def copycache(srcrepo, destrepo):
120 """Copy cached attributes from srcrepo to destrepo"""
121 destfilecache = destrepo._filecache
122 srcfilecache = srcrepo._filecache
123 if 'changelog' in srcfilecache:
124 destfilecache['changelog'] = ce = srcfilecache['changelog']
125 ce.obj.opener = ce.obj._realopener = destrepo.svfs
126 if 'obsstore' in srcfilecache:
127 destfilecache['obsstore'] = ce = srcfilecache['obsstore']
128 ce.obj.svfs = destrepo.svfs
129 if '_phasecache' in srcfilecache:
130 destfilecache['_phasecache'] = ce = srcfilecache['_phasecache']
131 ce.obj.opener = destrepo.svfs
@@ -1,673 +1,690 b''
1 1 # commandserver.py - communicate with Mercurial's API over a pipe
2 2 #
3 3 # Copyright Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import gc
12 12 import os
13 13 import random
14 14 import signal
15 15 import socket
16 16 import struct
17 17 import traceback
18 18
19 19 try:
20 20 import selectors
21 21 selectors.BaseSelector
22 22 except ImportError:
23 23 from .thirdparty import selectors2 as selectors
24 24
25 25 from .i18n import _
26 26 from . import (
27 27 encoding,
28 28 error,
29 29 loggingutil,
30 30 pycompat,
31 repocache,
31 32 util,
32 33 vfs as vfsmod,
33 34 )
34 35 from .utils import (
35 36 cborutil,
36 37 procutil,
37 38 )
38 39
39 40 class channeledoutput(object):
40 41 """
41 42 Write data to out in the following format:
42 43
43 44 data length (unsigned int),
44 45 data
45 46 """
46 47 def __init__(self, out, channel):
47 48 self.out = out
48 49 self.channel = channel
49 50
50 51 @property
51 52 def name(self):
52 53 return '<%c-channel>' % self.channel
53 54
54 55 def write(self, data):
55 56 if not data:
56 57 return
57 58 # single write() to guarantee the same atomicity as the underlying file
58 59 self.out.write(struct.pack('>cI', self.channel, len(data)) + data)
59 60 self.out.flush()
60 61
61 62 def __getattr__(self, attr):
62 63 if attr in (r'isatty', r'fileno', r'tell', r'seek'):
63 64 raise AttributeError(attr)
64 65 return getattr(self.out, attr)
65 66
66 67 class channeledmessage(object):
67 68 """
68 69 Write encoded message and metadata to out in the following format:
69 70
70 71 data length (unsigned int),
71 72 encoded message and metadata, as a flat key-value dict.
72 73
73 74 Each message should have 'type' attribute. Messages of unknown type
74 75 should be ignored.
75 76 """
76 77
77 78 # teach ui that write() can take **opts
78 79 structured = True
79 80
80 81 def __init__(self, out, channel, encodename, encodefn):
81 82 self._cout = channeledoutput(out, channel)
82 83 self.encoding = encodename
83 84 self._encodefn = encodefn
84 85
85 86 def write(self, data, **opts):
86 87 opts = pycompat.byteskwargs(opts)
87 88 if data is not None:
88 89 opts[b'data'] = data
89 90 self._cout.write(self._encodefn(opts))
90 91
91 92 def __getattr__(self, attr):
92 93 return getattr(self._cout, attr)
93 94
94 95 class channeledinput(object):
95 96 """
96 97 Read data from in_.
97 98
98 99 Requests for input are written to out in the following format:
99 100 channel identifier - 'I' for plain input, 'L' line based (1 byte)
100 101 how many bytes to send at most (unsigned int),
101 102
102 103 The client replies with:
103 104 data length (unsigned int), 0 meaning EOF
104 105 data
105 106 """
106 107
107 108 maxchunksize = 4 * 1024
108 109
109 110 def __init__(self, in_, out, channel):
110 111 self.in_ = in_
111 112 self.out = out
112 113 self.channel = channel
113 114
114 115 @property
115 116 def name(self):
116 117 return '<%c-channel>' % self.channel
117 118
118 119 def read(self, size=-1):
119 120 if size < 0:
120 121 # if we need to consume all the clients input, ask for 4k chunks
121 122 # so the pipe doesn't fill up risking a deadlock
122 123 size = self.maxchunksize
123 124 s = self._read(size, self.channel)
124 125 buf = s
125 126 while s:
126 127 s = self._read(size, self.channel)
127 128 buf += s
128 129
129 130 return buf
130 131 else:
131 132 return self._read(size, self.channel)
132 133
133 134 def _read(self, size, channel):
134 135 if not size:
135 136 return ''
136 137 assert size > 0
137 138
138 139 # tell the client we need at most size bytes
139 140 self.out.write(struct.pack('>cI', channel, size))
140 141 self.out.flush()
141 142
142 143 length = self.in_.read(4)
143 144 length = struct.unpack('>I', length)[0]
144 145 if not length:
145 146 return ''
146 147 else:
147 148 return self.in_.read(length)
148 149
149 150 def readline(self, size=-1):
150 151 if size < 0:
151 152 size = self.maxchunksize
152 153 s = self._read(size, 'L')
153 154 buf = s
154 155 # keep asking for more until there's either no more or
155 156 # we got a full line
156 157 while s and s[-1] != '\n':
157 158 s = self._read(size, 'L')
158 159 buf += s
159 160
160 161 return buf
161 162 else:
162 163 return self._read(size, 'L')
163 164
164 165 def __iter__(self):
165 166 return self
166 167
167 168 def next(self):
168 169 l = self.readline()
169 170 if not l:
170 171 raise StopIteration
171 172 return l
172 173
173 174 __next__ = next
174 175
175 176 def __getattr__(self, attr):
176 177 if attr in (r'isatty', r'fileno', r'tell', r'seek'):
177 178 raise AttributeError(attr)
178 179 return getattr(self.in_, attr)
179 180
180 181 _messageencoders = {
181 182 b'cbor': lambda v: b''.join(cborutil.streamencode(v)),
182 183 }
183 184
184 185 def _selectmessageencoder(ui):
185 186 # experimental config: cmdserver.message-encodings
186 187 encnames = ui.configlist(b'cmdserver', b'message-encodings')
187 188 for n in encnames:
188 189 f = _messageencoders.get(n)
189 190 if f:
190 191 return n, f
191 192 raise error.Abort(b'no supported message encodings: %s'
192 193 % b' '.join(encnames))
193 194
194 195 class server(object):
195 196 """
196 197 Listens for commands on fin, runs them and writes the output on a channel
197 198 based stream to fout.
198 199 """
199 200 def __init__(self, ui, repo, fin, fout, prereposetups=None):
200 201 self.cwd = encoding.getcwd()
201 202
202 203 if repo:
203 204 # the ui here is really the repo ui so take its baseui so we don't
204 205 # end up with its local configuration
205 206 self.ui = repo.baseui
206 207 self.repo = repo
207 208 self.repoui = repo.ui
208 209 else:
209 210 self.ui = ui
210 211 self.repo = self.repoui = None
211 212 self._prereposetups = prereposetups
212 213
213 214 self.cdebug = channeledoutput(fout, 'd')
214 215 self.cerr = channeledoutput(fout, 'e')
215 216 self.cout = channeledoutput(fout, 'o')
216 217 self.cin = channeledinput(fin, fout, 'I')
217 218 self.cresult = channeledoutput(fout, 'r')
218 219
219 220 if self.ui.config(b'cmdserver', b'log') == b'-':
220 221 # switch log stream of server's ui to the 'd' (debug) channel
221 222 # (don't touch repo.ui as its lifetime is longer than the server)
222 223 self.ui = self.ui.copy()
223 224 setuplogging(self.ui, repo=None, fp=self.cdebug)
224 225
225 226 # TODO: add this to help/config.txt when stabilized
226 227 # ``channel``
227 228 # Use separate channel for structured output. (Command-server only)
228 229 self.cmsg = None
229 230 if ui.config(b'ui', b'message-output') == b'channel':
230 231 encname, encfn = _selectmessageencoder(ui)
231 232 self.cmsg = channeledmessage(fout, b'm', encname, encfn)
232 233
233 234 self.client = fin
234 235
235 236 def cleanup(self):
236 237 """release and restore resources taken during server session"""
237 238
238 239 def _read(self, size):
239 240 if not size:
240 241 return ''
241 242
242 243 data = self.client.read(size)
243 244
244 245 # is the other end closed?
245 246 if not data:
246 247 raise EOFError
247 248
248 249 return data
249 250
250 251 def _readstr(self):
251 252 """read a string from the channel
252 253
253 254 format:
254 255 data length (uint32), data
255 256 """
256 257 length = struct.unpack('>I', self._read(4))[0]
257 258 if not length:
258 259 return ''
259 260 return self._read(length)
260 261
261 262 def _readlist(self):
262 263 """read a list of NULL separated strings from the channel"""
263 264 s = self._readstr()
264 265 if s:
265 266 return s.split('\0')
266 267 else:
267 268 return []
268 269
269 270 def runcommand(self):
270 271 """ reads a list of \0 terminated arguments, executes
271 272 and writes the return code to the result channel """
272 273 from . import dispatch # avoid cycle
273 274
274 275 args = self._readlist()
275 276
276 277 # copy the uis so changes (e.g. --config or --verbose) don't
277 278 # persist between requests
278 279 copiedui = self.ui.copy()
279 280 uis = [copiedui]
280 281 if self.repo:
281 282 self.repo.baseui = copiedui
282 283 # clone ui without using ui.copy because this is protected
283 284 repoui = self.repoui.__class__(self.repoui)
284 285 repoui.copy = copiedui.copy # redo copy protection
285 286 uis.append(repoui)
286 287 self.repo.ui = self.repo.dirstate._ui = repoui
287 288 self.repo.invalidateall()
288 289
289 290 for ui in uis:
290 291 ui.resetstate()
291 292 # any kind of interaction must use server channels, but chg may
292 293 # replace channels by fully functional tty files. so nontty is
293 294 # enforced only if cin is a channel.
294 295 if not util.safehasattr(self.cin, 'fileno'):
295 296 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
296 297
297 298 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
298 299 self.cout, self.cerr, self.cmsg,
299 300 prereposetups=self._prereposetups)
300 301
301 302 try:
302 303 ret = dispatch.dispatch(req) & 255
303 304 self.cresult.write(struct.pack('>i', int(ret)))
304 305 finally:
305 306 # restore old cwd
306 307 if '--cwd' in args:
307 308 os.chdir(self.cwd)
308 309
309 310 def getencoding(self):
310 311 """ writes the current encoding to the result channel """
311 312 self.cresult.write(encoding.encoding)
312 313
313 314 def serveone(self):
314 315 cmd = self.client.readline()[:-1]
315 316 if cmd:
316 317 handler = self.capabilities.get(cmd)
317 318 if handler:
318 319 handler(self)
319 320 else:
320 321 # clients are expected to check what commands are supported by
321 322 # looking at the servers capabilities
322 323 raise error.Abort(_('unknown command %s') % cmd)
323 324
324 325 return cmd != ''
325 326
326 327 capabilities = {'runcommand': runcommand,
327 328 'getencoding': getencoding}
328 329
329 330 def serve(self):
330 331 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
331 332 hellomsg += '\n'
332 333 hellomsg += 'encoding: ' + encoding.encoding
333 334 hellomsg += '\n'
334 335 if self.cmsg:
335 336 hellomsg += 'message-encoding: %s\n' % self.cmsg.encoding
336 337 hellomsg += 'pid: %d' % procutil.getpid()
337 338 if util.safehasattr(os, 'getpgid'):
338 339 hellomsg += '\n'
339 340 hellomsg += 'pgid: %d' % os.getpgid(0)
340 341
341 342 # write the hello msg in -one- chunk
342 343 self.cout.write(hellomsg)
343 344
344 345 try:
345 346 while self.serveone():
346 347 pass
347 348 except EOFError:
348 349 # we'll get here if the client disconnected while we were reading
349 350 # its request
350 351 return 1
351 352
352 353 return 0
353 354
354 355 def setuplogging(ui, repo=None, fp=None):
355 356 """Set up server logging facility
356 357
357 358 If cmdserver.log is '-', log messages will be sent to the given fp.
358 359 It should be the 'd' channel while a client is connected, and otherwise
359 360 is the stderr of the server process.
360 361 """
361 362 # developer config: cmdserver.log
362 363 logpath = ui.config(b'cmdserver', b'log')
363 364 if not logpath:
364 365 return
365 366 # developer config: cmdserver.track-log
366 367 tracked = set(ui.configlist(b'cmdserver', b'track-log'))
367 368
368 369 if logpath == b'-' and fp:
369 370 logger = loggingutil.fileobjectlogger(fp, tracked)
370 371 elif logpath == b'-':
371 372 logger = loggingutil.fileobjectlogger(ui.ferr, tracked)
372 373 else:
373 374 logpath = os.path.abspath(util.expandpath(logpath))
374 375 # developer config: cmdserver.max-log-files
375 376 maxfiles = ui.configint(b'cmdserver', b'max-log-files')
376 377 # developer config: cmdserver.max-log-size
377 378 maxsize = ui.configbytes(b'cmdserver', b'max-log-size')
378 379 vfs = vfsmod.vfs(os.path.dirname(logpath))
379 380 logger = loggingutil.filelogger(vfs, os.path.basename(logpath), tracked,
380 381 maxfiles=maxfiles, maxsize=maxsize)
381 382
382 383 targetuis = {ui}
383 384 if repo:
384 385 targetuis.add(repo.baseui)
385 386 targetuis.add(repo.ui)
386 387 for u in targetuis:
387 388 u.setlogger(b'cmdserver', logger)
388 389
389 390 class pipeservice(object):
390 391 def __init__(self, ui, repo, opts):
391 392 self.ui = ui
392 393 self.repo = repo
393 394
394 395 def init(self):
395 396 pass
396 397
397 398 def run(self):
398 399 ui = self.ui
399 400 # redirect stdio to null device so that broken extensions or in-process
400 401 # hooks will never cause corruption of channel protocol.
401 402 with procutil.protectedstdio(ui.fin, ui.fout) as (fin, fout):
402 403 sv = server(ui, self.repo, fin, fout)
403 404 try:
404 405 return sv.serve()
405 406 finally:
406 407 sv.cleanup()
407 408
408 409 def _initworkerprocess():
409 410 # use a different process group from the master process, in order to:
410 411 # 1. make the current process group no longer "orphaned" (because the
411 412 # parent of this process is in a different process group while
412 413 # remains in a same session)
413 414 # according to POSIX 2.2.2.52, orphaned process group will ignore
414 415 # terminal-generated stop signals like SIGTSTP (Ctrl+Z), which will
415 416 # cause trouble for things like ncurses.
416 417 # 2. the client can use kill(-pgid, sig) to simulate terminal-generated
417 418 # SIGINT (Ctrl+C) and process-exit-generated SIGHUP. our child
418 419 # processes like ssh will be killed properly, without affecting
419 420 # unrelated processes.
420 421 os.setpgid(0, 0)
421 422 # change random state otherwise forked request handlers would have a
422 423 # same state inherited from parent.
423 424 random.seed()
424 425
425 426 def _serverequest(ui, repo, conn, createcmdserver, prereposetups):
426 427 fin = conn.makefile(r'rb')
427 428 fout = conn.makefile(r'wb')
428 429 sv = None
429 430 try:
430 431 sv = createcmdserver(repo, conn, fin, fout, prereposetups)
431 432 try:
432 433 sv.serve()
433 434 # handle exceptions that may be raised by command server. most of
434 435 # known exceptions are caught by dispatch.
435 436 except error.Abort as inst:
436 437 ui.error(_('abort: %s\n') % inst)
437 438 except IOError as inst:
438 439 if inst.errno != errno.EPIPE:
439 440 raise
440 441 except KeyboardInterrupt:
441 442 pass
442 443 finally:
443 444 sv.cleanup()
444 445 except: # re-raises
445 446 # also write traceback to error channel. otherwise client cannot
446 447 # see it because it is written to server's stderr by default.
447 448 if sv:
448 449 cerr = sv.cerr
449 450 else:
450 451 cerr = channeledoutput(fout, 'e')
451 452 cerr.write(encoding.strtolocal(traceback.format_exc()))
452 453 raise
453 454 finally:
454 455 fin.close()
455 456 try:
456 457 fout.close() # implicit flush() may cause another EPIPE
457 458 except IOError as inst:
458 459 if inst.errno != errno.EPIPE:
459 460 raise
460 461
461 462 class unixservicehandler(object):
462 463 """Set of pluggable operations for unix-mode services
463 464
464 465 Almost all methods except for createcmdserver() are called in the main
465 466 process. You can't pass mutable resource back from createcmdserver().
466 467 """
467 468
468 469 pollinterval = None
469 470
470 471 def __init__(self, ui):
471 472 self.ui = ui
472 473
473 474 def bindsocket(self, sock, address):
474 475 util.bindunixsocket(sock, address)
475 476 sock.listen(socket.SOMAXCONN)
476 477 self.ui.status(_('listening at %s\n') % address)
477 478 self.ui.flush() # avoid buffering of status message
478 479
479 480 def unlinksocket(self, address):
480 481 os.unlink(address)
481 482
482 483 def shouldexit(self):
483 484 """True if server should shut down; checked per pollinterval"""
484 485 return False
485 486
486 487 def newconnection(self):
487 488 """Called when main process notices new connection"""
488 489
489 490 def createcmdserver(self, repo, conn, fin, fout, prereposetups):
490 491 """Create new command server instance; called in the process that
491 492 serves for the current connection"""
492 493 return server(self.ui, repo, fin, fout, prereposetups)
493 494
494 495 class unixforkingservice(object):
495 496 """
496 497 Listens on unix domain socket and forks server per connection
497 498 """
498 499
499 500 def __init__(self, ui, repo, opts, handler=None):
500 501 self.ui = ui
501 502 self.repo = repo
502 503 self.address = opts['address']
503 504 if not util.safehasattr(socket, 'AF_UNIX'):
504 505 raise error.Abort(_('unsupported platform'))
505 506 if not self.address:
506 507 raise error.Abort(_('no socket path specified with --address'))
507 508 self._servicehandler = handler or unixservicehandler(ui)
508 509 self._sock = None
509 510 self._mainipc = None
510 511 self._workeripc = None
511 512 self._oldsigchldhandler = None
512 513 self._workerpids = set() # updated by signal handler; do not iterate
513 514 self._socketunlinked = None
515 # experimental config: cmdserver.max-repo-cache
516 maxlen = ui.configint(b'cmdserver', b'max-repo-cache')
517 if maxlen < 0:
518 raise error.Abort(_('negative max-repo-cache size not allowed'))
519 self._repoloader = repocache.repoloader(ui, maxlen)
514 520
515 521 def init(self):
516 522 self._sock = socket.socket(socket.AF_UNIX)
517 523 # IPC channel from many workers to one main process; this is actually
518 524 # a uni-directional pipe, but is backed by a DGRAM socket so each
519 525 # message can be easily separated.
520 526 o = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM)
521 527 self._mainipc, self._workeripc = o
522 528 self._servicehandler.bindsocket(self._sock, self.address)
523 529 if util.safehasattr(procutil, 'unblocksignal'):
524 530 procutil.unblocksignal(signal.SIGCHLD)
525 531 o = signal.signal(signal.SIGCHLD, self._sigchldhandler)
526 532 self._oldsigchldhandler = o
527 533 self._socketunlinked = False
534 self._repoloader.start()
528 535
529 536 def _unlinksocket(self):
530 537 if not self._socketunlinked:
531 538 self._servicehandler.unlinksocket(self.address)
532 539 self._socketunlinked = True
533 540
534 541 def _cleanup(self):
535 542 signal.signal(signal.SIGCHLD, self._oldsigchldhandler)
536 543 self._sock.close()
537 544 self._mainipc.close()
538 545 self._workeripc.close()
539 546 self._unlinksocket()
547 self._repoloader.stop()
540 548 # don't kill child processes as they have active clients, just wait
541 549 self._reapworkers(0)
542 550
543 551 def run(self):
544 552 try:
545 553 self._mainloop()
546 554 finally:
547 555 self._cleanup()
548 556
549 557 def _mainloop(self):
550 558 exiting = False
551 559 h = self._servicehandler
552 560 selector = selectors.DefaultSelector()
553 561 selector.register(self._sock, selectors.EVENT_READ,
554 562 self._acceptnewconnection)
555 563 selector.register(self._mainipc, selectors.EVENT_READ,
556 564 self._handlemainipc)
557 565 while True:
558 566 if not exiting and h.shouldexit():
559 567 # clients can no longer connect() to the domain socket, so
560 568 # we stop queuing new requests.
561 569 # for requests that are queued (connect()-ed, but haven't been
562 570 # accept()-ed), handle them before exit. otherwise, clients
563 571 # waiting for recv() will receive ECONNRESET.
564 572 self._unlinksocket()
565 573 exiting = True
566 574 try:
567 575 events = selector.select(timeout=h.pollinterval)
568 576 except OSError as inst:
569 577 # selectors2 raises ETIMEDOUT if timeout exceeded while
570 578 # handling signal interrupt. That's probably wrong, but
571 579 # we can easily get around it.
572 580 if inst.errno != errno.ETIMEDOUT:
573 581 raise
574 582 events = []
575 583 if not events:
576 584 # only exit if we completed all queued requests
577 585 if exiting:
578 586 break
579 587 continue
580 588 for key, _mask in events:
581 589 key.data(key.fileobj, selector)
582 590 selector.close()
583 591
584 592 def _acceptnewconnection(self, sock, selector):
585 593 h = self._servicehandler
586 594 try:
587 595 conn, _addr = sock.accept()
588 596 except socket.error as inst:
589 597 if inst.args[0] == errno.EINTR:
590 598 return
591 599 raise
592 600
601 # Future improvement: On Python 3.7, maybe gc.freeze() can be used
602 # to prevent COW memory from being touched by GC.
603 # https://instagram-engineering.com/
604 # copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf
593 605 pid = os.fork()
594 606 if pid:
595 607 try:
596 608 self.ui.log(b'cmdserver', b'forked worker process (pid=%d)\n',
597 609 pid)
598 610 self._workerpids.add(pid)
599 611 h.newconnection()
600 612 finally:
601 613 conn.close() # release handle in parent process
602 614 else:
603 615 try:
604 616 selector.close()
605 617 sock.close()
606 618 self._mainipc.close()
607 619 self._runworker(conn)
608 620 conn.close()
609 621 self._workeripc.close()
610 622 os._exit(0)
611 623 except: # never return, hence no re-raises
612 624 try:
613 625 self.ui.traceback(force=True)
614 626 finally:
615 627 os._exit(255)
616 628
617 629 def _handlemainipc(self, sock, selector):
618 630 """Process messages sent from a worker"""
619 631 try:
620 632 path = sock.recv(32768) # large enough to receive path
621 633 except socket.error as inst:
622 634 if inst.args[0] == errno.EINTR:
623 635 return
624 636 raise
625
626 self.ui.log(b'cmdserver', b'repository: %s\n', path)
637 self._repoloader.load(path)
627 638
628 639 def _sigchldhandler(self, signal, frame):
629 640 self._reapworkers(os.WNOHANG)
630 641
631 642 def _reapworkers(self, options):
632 643 while self._workerpids:
633 644 try:
634 645 pid, _status = os.waitpid(-1, options)
635 646 except OSError as inst:
636 647 if inst.errno == errno.EINTR:
637 648 continue
638 649 if inst.errno != errno.ECHILD:
639 650 raise
640 651 # no child processes at all (reaped by other waitpid()?)
641 652 self._workerpids.clear()
642 653 return
643 654 if pid == 0:
644 655 # no waitable child processes
645 656 return
646 657 self.ui.log(b'cmdserver', b'worker process exited (pid=%d)\n', pid)
647 658 self._workerpids.discard(pid)
648 659
649 660 def _runworker(self, conn):
650 661 signal.signal(signal.SIGCHLD, self._oldsigchldhandler)
651 662 _initworkerprocess()
652 663 h = self._servicehandler
653 664 try:
654 665 _serverequest(self.ui, self.repo, conn, h.createcmdserver,
655 666 prereposetups=[self._reposetup])
656 667 finally:
657 668 gc.collect() # trigger __del__ since worker process uses os._exit
658 669
659 670 def _reposetup(self, ui, repo):
660 671 if not repo.local():
661 672 return
662 673
663 674 class unixcmdserverrepo(repo.__class__):
664 675 def close(self):
665 676 super(unixcmdserverrepo, self).close()
666 677 try:
667 678 self._cmdserveripc.send(self.root)
668 679 except socket.error:
669 680 self.ui.log(b'cmdserver',
670 681 b'failed to send repo root to master\n')
671 682
672 683 repo.__class__ = unixcmdserverrepo
673 684 repo._cmdserveripc = self._workeripc
685
686 cachedrepo = self._repoloader.get(repo.root)
687 if cachedrepo is None:
688 return
689 repo.ui.log(b'repocache', b'repo from cache: %s\n', repo.root)
690 repocache.copycache(cachedrepo, repo)
@@ -1,1463 +1,1466 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import functools
11 11 import re
12 12
13 13 from . import (
14 14 encoding,
15 15 error,
16 16 )
17 17
18 18 def loadconfigtable(ui, extname, configtable):
19 19 """update config item known to the ui with the extension ones"""
20 20 for section, items in sorted(configtable.items()):
21 21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 22 knownkeys = set(knownitems)
23 23 newkeys = set(items)
24 24 for key in sorted(knownkeys & newkeys):
25 25 msg = "extension '%s' overwrite config item '%s.%s'"
26 26 msg %= (extname, section, key)
27 27 ui.develwarn(msg, config='warn-config')
28 28
29 29 knownitems.update(items)
30 30
31 31 class configitem(object):
32 32 """represent a known config item
33 33
34 34 :section: the official config section where to find this item,
35 35 :name: the official name within the section,
36 36 :default: default value for this item,
37 37 :alias: optional list of tuples as alternatives,
38 38 :generic: this is a generic definition, match name using regular expression.
39 39 """
40 40
41 41 def __init__(self, section, name, default=None, alias=(),
42 42 generic=False, priority=0):
43 43 self.section = section
44 44 self.name = name
45 45 self.default = default
46 46 self.alias = list(alias)
47 47 self.generic = generic
48 48 self.priority = priority
49 49 self._re = None
50 50 if generic:
51 51 self._re = re.compile(self.name)
52 52
53 53 class itemregister(dict):
54 54 """A specialized dictionary that can handle wild-card selection"""
55 55
56 56 def __init__(self):
57 57 super(itemregister, self).__init__()
58 58 self._generics = set()
59 59
60 60 def update(self, other):
61 61 super(itemregister, self).update(other)
62 62 self._generics.update(other._generics)
63 63
64 64 def __setitem__(self, key, item):
65 65 super(itemregister, self).__setitem__(key, item)
66 66 if item.generic:
67 67 self._generics.add(item)
68 68
69 69 def get(self, key):
70 70 baseitem = super(itemregister, self).get(key)
71 71 if baseitem is not None and not baseitem.generic:
72 72 return baseitem
73 73
74 74 # search for a matching generic item
75 75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
76 76 for item in generics:
77 77 # we use 'match' instead of 'search' to make the matching simpler
78 78 # for people unfamiliar with regular expression. Having the match
79 79 # rooted to the start of the string will produce less surprising
80 80 # result for user writing simple regex for sub-attribute.
81 81 #
82 82 # For example using "color\..*" match produces an unsurprising
83 83 # result, while using search could suddenly match apparently
84 84 # unrelated configuration that happens to contains "color."
85 85 # anywhere. This is a tradeoff where we favor requiring ".*" on
86 86 # some match to avoid the need to prefix most pattern with "^".
87 87 # The "^" seems more error prone.
88 88 if item._re.match(key):
89 89 return item
90 90
91 91 return None
92 92
93 93 coreitems = {}
94 94
95 95 def _register(configtable, *args, **kwargs):
96 96 item = configitem(*args, **kwargs)
97 97 section = configtable.setdefault(item.section, itemregister())
98 98 if item.name in section:
99 99 msg = "duplicated config item registration for '%s.%s'"
100 100 raise error.ProgrammingError(msg % (item.section, item.name))
101 101 section[item.name] = item
102 102
103 103 # special value for case where the default is derived from other values
104 104 dynamicdefault = object()
105 105
106 106 # Registering actual config items
107 107
108 108 def getitemregister(configtable):
109 109 f = functools.partial(_register, configtable)
110 110 # export pseudo enum as configitem.*
111 111 f.dynamicdefault = dynamicdefault
112 112 return f
113 113
114 114 coreconfigitem = getitemregister(coreitems)
115 115
116 116 coreconfigitem('alias', '.*',
117 117 default=dynamicdefault,
118 118 generic=True,
119 119 )
120 120 coreconfigitem('annotate', 'nodates',
121 121 default=False,
122 122 )
123 123 coreconfigitem('annotate', 'showfunc',
124 124 default=False,
125 125 )
126 126 coreconfigitem('annotate', 'unified',
127 127 default=None,
128 128 )
129 129 coreconfigitem('annotate', 'git',
130 130 default=False,
131 131 )
132 132 coreconfigitem('annotate', 'ignorews',
133 133 default=False,
134 134 )
135 135 coreconfigitem('annotate', 'ignorewsamount',
136 136 default=False,
137 137 )
138 138 coreconfigitem('annotate', 'ignoreblanklines',
139 139 default=False,
140 140 )
141 141 coreconfigitem('annotate', 'ignorewseol',
142 142 default=False,
143 143 )
144 144 coreconfigitem('annotate', 'nobinary',
145 145 default=False,
146 146 )
147 147 coreconfigitem('annotate', 'noprefix',
148 148 default=False,
149 149 )
150 150 coreconfigitem('annotate', 'word-diff',
151 151 default=False,
152 152 )
153 153 coreconfigitem('auth', 'cookiefile',
154 154 default=None,
155 155 )
156 156 # bookmarks.pushing: internal hack for discovery
157 157 coreconfigitem('bookmarks', 'pushing',
158 158 default=list,
159 159 )
160 160 # bundle.mainreporoot: internal hack for bundlerepo
161 161 coreconfigitem('bundle', 'mainreporoot',
162 162 default='',
163 163 )
164 164 coreconfigitem('censor', 'policy',
165 165 default='abort',
166 166 )
167 167 coreconfigitem('chgserver', 'idletimeout',
168 168 default=3600,
169 169 )
170 170 coreconfigitem('chgserver', 'skiphash',
171 171 default=False,
172 172 )
173 173 coreconfigitem('cmdserver', 'log',
174 174 default=None,
175 175 )
176 176 coreconfigitem('cmdserver', 'max-log-files',
177 177 default=7,
178 178 )
179 179 coreconfigitem('cmdserver', 'max-log-size',
180 180 default='1 MB',
181 181 )
182 coreconfigitem('cmdserver', 'max-repo-cache',
183 default=0,
184 )
182 185 coreconfigitem('cmdserver', 'message-encodings',
183 186 default=list,
184 187 )
185 188 coreconfigitem('cmdserver', 'track-log',
186 default=lambda: ['chgserver', 'cmdserver'],
189 default=lambda: ['chgserver', 'cmdserver', 'repocache'],
187 190 )
188 191 coreconfigitem('color', '.*',
189 192 default=None,
190 193 generic=True,
191 194 )
192 195 coreconfigitem('color', 'mode',
193 196 default='auto',
194 197 )
195 198 coreconfigitem('color', 'pagermode',
196 199 default=dynamicdefault,
197 200 )
198 201 coreconfigitem('commands', 'grep.all-files',
199 202 default=False,
200 203 )
201 204 coreconfigitem('commands', 'resolve.confirm',
202 205 default=False,
203 206 )
204 207 coreconfigitem('commands', 'resolve.explicit-re-merge',
205 208 default=False,
206 209 )
207 210 coreconfigitem('commands', 'resolve.mark-check',
208 211 default='none',
209 212 )
210 213 coreconfigitem('commands', 'show.aliasprefix',
211 214 default=list,
212 215 )
213 216 coreconfigitem('commands', 'status.relative',
214 217 default=False,
215 218 )
216 219 coreconfigitem('commands', 'status.skipstates',
217 220 default=[],
218 221 )
219 222 coreconfigitem('commands', 'status.terse',
220 223 default='',
221 224 )
222 225 coreconfigitem('commands', 'status.verbose',
223 226 default=False,
224 227 )
225 228 coreconfigitem('commands', 'update.check',
226 229 default=None,
227 230 )
228 231 coreconfigitem('commands', 'update.requiredest',
229 232 default=False,
230 233 )
231 234 coreconfigitem('committemplate', '.*',
232 235 default=None,
233 236 generic=True,
234 237 )
235 238 coreconfigitem('convert', 'bzr.saverev',
236 239 default=True,
237 240 )
238 241 coreconfigitem('convert', 'cvsps.cache',
239 242 default=True,
240 243 )
241 244 coreconfigitem('convert', 'cvsps.fuzz',
242 245 default=60,
243 246 )
244 247 coreconfigitem('convert', 'cvsps.logencoding',
245 248 default=None,
246 249 )
247 250 coreconfigitem('convert', 'cvsps.mergefrom',
248 251 default=None,
249 252 )
250 253 coreconfigitem('convert', 'cvsps.mergeto',
251 254 default=None,
252 255 )
253 256 coreconfigitem('convert', 'git.committeractions',
254 257 default=lambda: ['messagedifferent'],
255 258 )
256 259 coreconfigitem('convert', 'git.extrakeys',
257 260 default=list,
258 261 )
259 262 coreconfigitem('convert', 'git.findcopiesharder',
260 263 default=False,
261 264 )
262 265 coreconfigitem('convert', 'git.remoteprefix',
263 266 default='remote',
264 267 )
265 268 coreconfigitem('convert', 'git.renamelimit',
266 269 default=400,
267 270 )
268 271 coreconfigitem('convert', 'git.saverev',
269 272 default=True,
270 273 )
271 274 coreconfigitem('convert', 'git.similarity',
272 275 default=50,
273 276 )
274 277 coreconfigitem('convert', 'git.skipsubmodules',
275 278 default=False,
276 279 )
277 280 coreconfigitem('convert', 'hg.clonebranches',
278 281 default=False,
279 282 )
280 283 coreconfigitem('convert', 'hg.ignoreerrors',
281 284 default=False,
282 285 )
283 286 coreconfigitem('convert', 'hg.revs',
284 287 default=None,
285 288 )
286 289 coreconfigitem('convert', 'hg.saverev',
287 290 default=False,
288 291 )
289 292 coreconfigitem('convert', 'hg.sourcename',
290 293 default=None,
291 294 )
292 295 coreconfigitem('convert', 'hg.startrev',
293 296 default=None,
294 297 )
295 298 coreconfigitem('convert', 'hg.tagsbranch',
296 299 default='default',
297 300 )
298 301 coreconfigitem('convert', 'hg.usebranchnames',
299 302 default=True,
300 303 )
301 304 coreconfigitem('convert', 'ignoreancestorcheck',
302 305 default=False,
303 306 )
304 307 coreconfigitem('convert', 'localtimezone',
305 308 default=False,
306 309 )
307 310 coreconfigitem('convert', 'p4.encoding',
308 311 default=dynamicdefault,
309 312 )
310 313 coreconfigitem('convert', 'p4.startrev',
311 314 default=0,
312 315 )
313 316 coreconfigitem('convert', 'skiptags',
314 317 default=False,
315 318 )
316 319 coreconfigitem('convert', 'svn.debugsvnlog',
317 320 default=True,
318 321 )
319 322 coreconfigitem('convert', 'svn.trunk',
320 323 default=None,
321 324 )
322 325 coreconfigitem('convert', 'svn.tags',
323 326 default=None,
324 327 )
325 328 coreconfigitem('convert', 'svn.branches',
326 329 default=None,
327 330 )
328 331 coreconfigitem('convert', 'svn.startrev',
329 332 default=0,
330 333 )
331 334 coreconfigitem('debug', 'dirstate.delaywrite',
332 335 default=0,
333 336 )
334 337 coreconfigitem('defaults', '.*',
335 338 default=None,
336 339 generic=True,
337 340 )
338 341 coreconfigitem('devel', 'all-warnings',
339 342 default=False,
340 343 )
341 344 coreconfigitem('devel', 'bundle2.debug',
342 345 default=False,
343 346 )
344 347 coreconfigitem('devel', 'bundle.delta',
345 348 default='',
346 349 )
347 350 coreconfigitem('devel', 'cache-vfs',
348 351 default=None,
349 352 )
350 353 coreconfigitem('devel', 'check-locks',
351 354 default=False,
352 355 )
353 356 coreconfigitem('devel', 'check-relroot',
354 357 default=False,
355 358 )
356 359 coreconfigitem('devel', 'default-date',
357 360 default=None,
358 361 )
359 362 coreconfigitem('devel', 'deprec-warn',
360 363 default=False,
361 364 )
362 365 coreconfigitem('devel', 'disableloaddefaultcerts',
363 366 default=False,
364 367 )
365 368 coreconfigitem('devel', 'warn-empty-changegroup',
366 369 default=False,
367 370 )
368 371 coreconfigitem('devel', 'legacy.exchange',
369 372 default=list,
370 373 )
371 374 coreconfigitem('devel', 'servercafile',
372 375 default='',
373 376 )
374 377 coreconfigitem('devel', 'serverexactprotocol',
375 378 default='',
376 379 )
377 380 coreconfigitem('devel', 'serverrequirecert',
378 381 default=False,
379 382 )
380 383 coreconfigitem('devel', 'strip-obsmarkers',
381 384 default=True,
382 385 )
383 386 coreconfigitem('devel', 'warn-config',
384 387 default=None,
385 388 )
386 389 coreconfigitem('devel', 'warn-config-default',
387 390 default=None,
388 391 )
389 392 coreconfigitem('devel', 'user.obsmarker',
390 393 default=None,
391 394 )
392 395 coreconfigitem('devel', 'warn-config-unknown',
393 396 default=None,
394 397 )
395 398 coreconfigitem('devel', 'debug.copies',
396 399 default=False,
397 400 )
398 401 coreconfigitem('devel', 'debug.extensions',
399 402 default=False,
400 403 )
401 404 coreconfigitem('devel', 'debug.peer-request',
402 405 default=False,
403 406 )
404 407 coreconfigitem('diff', 'nodates',
405 408 default=False,
406 409 )
407 410 coreconfigitem('diff', 'showfunc',
408 411 default=False,
409 412 )
410 413 coreconfigitem('diff', 'unified',
411 414 default=None,
412 415 )
413 416 coreconfigitem('diff', 'git',
414 417 default=False,
415 418 )
416 419 coreconfigitem('diff', 'ignorews',
417 420 default=False,
418 421 )
419 422 coreconfigitem('diff', 'ignorewsamount',
420 423 default=False,
421 424 )
422 425 coreconfigitem('diff', 'ignoreblanklines',
423 426 default=False,
424 427 )
425 428 coreconfigitem('diff', 'ignorewseol',
426 429 default=False,
427 430 )
428 431 coreconfigitem('diff', 'nobinary',
429 432 default=False,
430 433 )
431 434 coreconfigitem('diff', 'noprefix',
432 435 default=False,
433 436 )
434 437 coreconfigitem('diff', 'word-diff',
435 438 default=False,
436 439 )
437 440 coreconfigitem('email', 'bcc',
438 441 default=None,
439 442 )
440 443 coreconfigitem('email', 'cc',
441 444 default=None,
442 445 )
443 446 coreconfigitem('email', 'charsets',
444 447 default=list,
445 448 )
446 449 coreconfigitem('email', 'from',
447 450 default=None,
448 451 )
449 452 coreconfigitem('email', 'method',
450 453 default='smtp',
451 454 )
452 455 coreconfigitem('email', 'reply-to',
453 456 default=None,
454 457 )
455 458 coreconfigitem('email', 'to',
456 459 default=None,
457 460 )
458 461 coreconfigitem('experimental', 'archivemetatemplate',
459 462 default=dynamicdefault,
460 463 )
461 464 coreconfigitem('experimental', 'auto-publish',
462 465 default='publish',
463 466 )
464 467 coreconfigitem('experimental', 'bundle-phases',
465 468 default=False,
466 469 )
467 470 coreconfigitem('experimental', 'bundle2-advertise',
468 471 default=True,
469 472 )
470 473 coreconfigitem('experimental', 'bundle2-output-capture',
471 474 default=False,
472 475 )
473 476 coreconfigitem('experimental', 'bundle2.pushback',
474 477 default=False,
475 478 )
476 479 coreconfigitem('experimental', 'bundle2lazylocking',
477 480 default=False,
478 481 )
479 482 coreconfigitem('experimental', 'bundlecomplevel',
480 483 default=None,
481 484 )
482 485 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
483 486 default=None,
484 487 )
485 488 coreconfigitem('experimental', 'bundlecomplevel.gzip',
486 489 default=None,
487 490 )
488 491 coreconfigitem('experimental', 'bundlecomplevel.none',
489 492 default=None,
490 493 )
491 494 coreconfigitem('experimental', 'bundlecomplevel.zstd',
492 495 default=None,
493 496 )
494 497 coreconfigitem('experimental', 'changegroup3',
495 498 default=False,
496 499 )
497 500 coreconfigitem('experimental', 'clientcompressionengines',
498 501 default=list,
499 502 )
500 503 coreconfigitem('experimental', 'copytrace',
501 504 default='on',
502 505 )
503 506 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
504 507 default=100,
505 508 )
506 509 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
507 510 default=100,
508 511 )
509 512 coreconfigitem('experimental', 'crecordtest',
510 513 default=None,
511 514 )
512 515 coreconfigitem('experimental', 'directaccess',
513 516 default=False,
514 517 )
515 518 coreconfigitem('experimental', 'directaccess.revnums',
516 519 default=False,
517 520 )
518 521 coreconfigitem('experimental', 'editortmpinhg',
519 522 default=False,
520 523 )
521 524 coreconfigitem('experimental', 'evolution',
522 525 default=list,
523 526 )
524 527 coreconfigitem('experimental', 'evolution.allowdivergence',
525 528 default=False,
526 529 alias=[('experimental', 'allowdivergence')]
527 530 )
528 531 coreconfigitem('experimental', 'evolution.allowunstable',
529 532 default=None,
530 533 )
531 534 coreconfigitem('experimental', 'evolution.createmarkers',
532 535 default=None,
533 536 )
534 537 coreconfigitem('experimental', 'evolution.effect-flags',
535 538 default=True,
536 539 alias=[('experimental', 'effect-flags')]
537 540 )
538 541 coreconfigitem('experimental', 'evolution.exchange',
539 542 default=None,
540 543 )
541 544 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
542 545 default=False,
543 546 )
544 547 coreconfigitem('experimental', 'evolution.report-instabilities',
545 548 default=True,
546 549 )
547 550 coreconfigitem('experimental', 'evolution.track-operation',
548 551 default=True,
549 552 )
550 553 coreconfigitem('experimental', 'maxdeltachainspan',
551 554 default=-1,
552 555 )
553 556 coreconfigitem('experimental', 'mergetempdirprefix',
554 557 default=None,
555 558 )
556 559 coreconfigitem('experimental', 'narrow',
557 560 default=False,
558 561 )
559 562 coreconfigitem('experimental', 'nonnormalparanoidcheck',
560 563 default=False,
561 564 )
562 565 coreconfigitem('experimental', 'exportableenviron',
563 566 default=list,
564 567 )
565 568 coreconfigitem('experimental', 'extendedheader.index',
566 569 default=None,
567 570 )
568 571 coreconfigitem('experimental', 'extendedheader.similarity',
569 572 default=False,
570 573 )
571 574 coreconfigitem('experimental', 'format.compression',
572 575 default='zlib',
573 576 )
574 577 coreconfigitem('experimental', 'graphshorten',
575 578 default=False,
576 579 )
577 580 coreconfigitem('experimental', 'graphstyle.parent',
578 581 default=dynamicdefault,
579 582 )
580 583 coreconfigitem('experimental', 'graphstyle.missing',
581 584 default=dynamicdefault,
582 585 )
583 586 coreconfigitem('experimental', 'graphstyle.grandparent',
584 587 default=dynamicdefault,
585 588 )
586 589 coreconfigitem('experimental', 'hook-track-tags',
587 590 default=False,
588 591 )
589 592 coreconfigitem('experimental', 'httppeer.advertise-v2',
590 593 default=False,
591 594 )
592 595 coreconfigitem('experimental', 'httppeer.v2-encoder-order',
593 596 default=None,
594 597 )
595 598 coreconfigitem('experimental', 'httppostargs',
596 599 default=False,
597 600 )
598 601 coreconfigitem('experimental', 'mergedriver',
599 602 default=None,
600 603 )
601 604 coreconfigitem('experimental', 'nointerrupt', default=False)
602 605 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
603 606
604 607 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
605 608 default=False,
606 609 )
607 610 coreconfigitem('experimental', 'remotenames',
608 611 default=False,
609 612 )
610 613 coreconfigitem('experimental', 'removeemptydirs',
611 614 default=True,
612 615 )
613 616 coreconfigitem('experimental', 'revisions.prefixhexnode',
614 617 default=False,
615 618 )
616 619 coreconfigitem('experimental', 'revlogv2',
617 620 default=None,
618 621 )
619 622 coreconfigitem('experimental', 'revisions.disambiguatewithin',
620 623 default=None,
621 624 )
622 625 coreconfigitem('experimental', 'server.filesdata.recommended-batch-size',
623 626 default=50000,
624 627 )
625 628 coreconfigitem('experimental', 'server.manifestdata.recommended-batch-size',
626 629 default=100000,
627 630 )
628 631 coreconfigitem('experimental', 'server.stream-narrow-clones',
629 632 default=False,
630 633 )
631 634 coreconfigitem('experimental', 'single-head-per-branch',
632 635 default=False,
633 636 )
634 637 coreconfigitem('experimental', 'sshserver.support-v2',
635 638 default=False,
636 639 )
637 640 coreconfigitem('experimental', 'sparse-read',
638 641 default=False,
639 642 )
640 643 coreconfigitem('experimental', 'sparse-read.density-threshold',
641 644 default=0.50,
642 645 )
643 646 coreconfigitem('experimental', 'sparse-read.min-gap-size',
644 647 default='65K',
645 648 )
646 649 coreconfigitem('experimental', 'treemanifest',
647 650 default=False,
648 651 )
649 652 coreconfigitem('experimental', 'update.atomic-file',
650 653 default=False,
651 654 )
652 655 coreconfigitem('experimental', 'sshpeer.advertise-v2',
653 656 default=False,
654 657 )
655 658 coreconfigitem('experimental', 'web.apiserver',
656 659 default=False,
657 660 )
658 661 coreconfigitem('experimental', 'web.api.http-v2',
659 662 default=False,
660 663 )
661 664 coreconfigitem('experimental', 'web.api.debugreflect',
662 665 default=False,
663 666 )
664 667 coreconfigitem('experimental', 'worker.wdir-get-thread-safe',
665 668 default=False,
666 669 )
667 670 coreconfigitem('experimental', 'xdiff',
668 671 default=False,
669 672 )
670 673 coreconfigitem('extensions', '.*',
671 674 default=None,
672 675 generic=True,
673 676 )
674 677 coreconfigitem('extdata', '.*',
675 678 default=None,
676 679 generic=True,
677 680 )
678 681 coreconfigitem('format', 'chunkcachesize',
679 682 default=None,
680 683 )
681 684 coreconfigitem('format', 'dotencode',
682 685 default=True,
683 686 )
684 687 coreconfigitem('format', 'generaldelta',
685 688 default=False,
686 689 )
687 690 coreconfigitem('format', 'manifestcachesize',
688 691 default=None,
689 692 )
690 693 coreconfigitem('format', 'maxchainlen',
691 694 default=dynamicdefault,
692 695 )
693 696 coreconfigitem('format', 'obsstore-version',
694 697 default=None,
695 698 )
696 699 coreconfigitem('format', 'sparse-revlog',
697 700 default=True,
698 701 )
699 702 coreconfigitem('format', 'usefncache',
700 703 default=True,
701 704 )
702 705 coreconfigitem('format', 'usegeneraldelta',
703 706 default=True,
704 707 )
705 708 coreconfigitem('format', 'usestore',
706 709 default=True,
707 710 )
708 711 coreconfigitem('format', 'internal-phase',
709 712 default=False,
710 713 )
711 714 coreconfigitem('fsmonitor', 'warn_when_unused',
712 715 default=True,
713 716 )
714 717 coreconfigitem('fsmonitor', 'warn_update_file_count',
715 718 default=50000,
716 719 )
717 720 coreconfigitem('help', 'hidden-command\..*',
718 721 default=False,
719 722 generic=True,
720 723 )
721 724 coreconfigitem('help', 'hidden-topic\..*',
722 725 default=False,
723 726 generic=True,
724 727 )
725 728 coreconfigitem('hooks', '.*',
726 729 default=dynamicdefault,
727 730 generic=True,
728 731 )
729 732 coreconfigitem('hgweb-paths', '.*',
730 733 default=list,
731 734 generic=True,
732 735 )
733 736 coreconfigitem('hostfingerprints', '.*',
734 737 default=list,
735 738 generic=True,
736 739 )
737 740 coreconfigitem('hostsecurity', 'ciphers',
738 741 default=None,
739 742 )
740 743 coreconfigitem('hostsecurity', 'disabletls10warning',
741 744 default=False,
742 745 )
743 746 coreconfigitem('hostsecurity', 'minimumprotocol',
744 747 default=dynamicdefault,
745 748 )
746 749 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
747 750 default=dynamicdefault,
748 751 generic=True,
749 752 )
750 753 coreconfigitem('hostsecurity', '.*:ciphers$',
751 754 default=dynamicdefault,
752 755 generic=True,
753 756 )
754 757 coreconfigitem('hostsecurity', '.*:fingerprints$',
755 758 default=list,
756 759 generic=True,
757 760 )
758 761 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
759 762 default=None,
760 763 generic=True,
761 764 )
762 765
763 766 coreconfigitem('http_proxy', 'always',
764 767 default=False,
765 768 )
766 769 coreconfigitem('http_proxy', 'host',
767 770 default=None,
768 771 )
769 772 coreconfigitem('http_proxy', 'no',
770 773 default=list,
771 774 )
772 775 coreconfigitem('http_proxy', 'passwd',
773 776 default=None,
774 777 )
775 778 coreconfigitem('http_proxy', 'user',
776 779 default=None,
777 780 )
778 781
779 782 coreconfigitem('http', 'timeout',
780 783 default=None,
781 784 )
782 785
783 786 coreconfigitem('logtoprocess', 'commandexception',
784 787 default=None,
785 788 )
786 789 coreconfigitem('logtoprocess', 'commandfinish',
787 790 default=None,
788 791 )
789 792 coreconfigitem('logtoprocess', 'command',
790 793 default=None,
791 794 )
792 795 coreconfigitem('logtoprocess', 'develwarn',
793 796 default=None,
794 797 )
795 798 coreconfigitem('logtoprocess', 'uiblocked',
796 799 default=None,
797 800 )
798 801 coreconfigitem('merge', 'checkunknown',
799 802 default='abort',
800 803 )
801 804 coreconfigitem('merge', 'checkignored',
802 805 default='abort',
803 806 )
804 807 coreconfigitem('experimental', 'merge.checkpathconflicts',
805 808 default=False,
806 809 )
807 810 coreconfigitem('merge', 'followcopies',
808 811 default=True,
809 812 )
810 813 coreconfigitem('merge', 'on-failure',
811 814 default='continue',
812 815 )
813 816 coreconfigitem('merge', 'preferancestor',
814 817 default=lambda: ['*'],
815 818 )
816 819 coreconfigitem('merge', 'strict-capability-check',
817 820 default=False,
818 821 )
819 822 coreconfigitem('merge-tools', '.*',
820 823 default=None,
821 824 generic=True,
822 825 )
823 826 coreconfigitem('merge-tools', br'.*\.args$',
824 827 default="$local $base $other",
825 828 generic=True,
826 829 priority=-1,
827 830 )
828 831 coreconfigitem('merge-tools', br'.*\.binary$',
829 832 default=False,
830 833 generic=True,
831 834 priority=-1,
832 835 )
833 836 coreconfigitem('merge-tools', br'.*\.check$',
834 837 default=list,
835 838 generic=True,
836 839 priority=-1,
837 840 )
838 841 coreconfigitem('merge-tools', br'.*\.checkchanged$',
839 842 default=False,
840 843 generic=True,
841 844 priority=-1,
842 845 )
843 846 coreconfigitem('merge-tools', br'.*\.executable$',
844 847 default=dynamicdefault,
845 848 generic=True,
846 849 priority=-1,
847 850 )
848 851 coreconfigitem('merge-tools', br'.*\.fixeol$',
849 852 default=False,
850 853 generic=True,
851 854 priority=-1,
852 855 )
853 856 coreconfigitem('merge-tools', br'.*\.gui$',
854 857 default=False,
855 858 generic=True,
856 859 priority=-1,
857 860 )
858 861 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
859 862 default='basic',
860 863 generic=True,
861 864 priority=-1,
862 865 )
863 866 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
864 867 default=dynamicdefault, # take from ui.mergemarkertemplate
865 868 generic=True,
866 869 priority=-1,
867 870 )
868 871 coreconfigitem('merge-tools', br'.*\.priority$',
869 872 default=0,
870 873 generic=True,
871 874 priority=-1,
872 875 )
873 876 coreconfigitem('merge-tools', br'.*\.premerge$',
874 877 default=dynamicdefault,
875 878 generic=True,
876 879 priority=-1,
877 880 )
878 881 coreconfigitem('merge-tools', br'.*\.symlink$',
879 882 default=False,
880 883 generic=True,
881 884 priority=-1,
882 885 )
883 886 coreconfigitem('pager', 'attend-.*',
884 887 default=dynamicdefault,
885 888 generic=True,
886 889 )
887 890 coreconfigitem('pager', 'ignore',
888 891 default=list,
889 892 )
890 893 coreconfigitem('pager', 'pager',
891 894 default=dynamicdefault,
892 895 )
893 896 coreconfigitem('patch', 'eol',
894 897 default='strict',
895 898 )
896 899 coreconfigitem('patch', 'fuzz',
897 900 default=2,
898 901 )
899 902 coreconfigitem('paths', 'default',
900 903 default=None,
901 904 )
902 905 coreconfigitem('paths', 'default-push',
903 906 default=None,
904 907 )
905 908 coreconfigitem('paths', '.*',
906 909 default=None,
907 910 generic=True,
908 911 )
909 912 coreconfigitem('phases', 'checksubrepos',
910 913 default='follow',
911 914 )
912 915 coreconfigitem('phases', 'new-commit',
913 916 default='draft',
914 917 )
915 918 coreconfigitem('phases', 'publish',
916 919 default=True,
917 920 )
918 921 coreconfigitem('profiling', 'enabled',
919 922 default=False,
920 923 )
921 924 coreconfigitem('profiling', 'format',
922 925 default='text',
923 926 )
924 927 coreconfigitem('profiling', 'freq',
925 928 default=1000,
926 929 )
927 930 coreconfigitem('profiling', 'limit',
928 931 default=30,
929 932 )
930 933 coreconfigitem('profiling', 'nested',
931 934 default=0,
932 935 )
933 936 coreconfigitem('profiling', 'output',
934 937 default=None,
935 938 )
936 939 coreconfigitem('profiling', 'showmax',
937 940 default=0.999,
938 941 )
939 942 coreconfigitem('profiling', 'showmin',
940 943 default=dynamicdefault,
941 944 )
942 945 coreconfigitem('profiling', 'sort',
943 946 default='inlinetime',
944 947 )
945 948 coreconfigitem('profiling', 'statformat',
946 949 default='hotpath',
947 950 )
948 951 coreconfigitem('profiling', 'time-track',
949 952 default=dynamicdefault,
950 953 )
951 954 coreconfigitem('profiling', 'type',
952 955 default='stat',
953 956 )
954 957 coreconfigitem('progress', 'assume-tty',
955 958 default=False,
956 959 )
957 960 coreconfigitem('progress', 'changedelay',
958 961 default=1,
959 962 )
960 963 coreconfigitem('progress', 'clear-complete',
961 964 default=True,
962 965 )
963 966 coreconfigitem('progress', 'debug',
964 967 default=False,
965 968 )
966 969 coreconfigitem('progress', 'delay',
967 970 default=3,
968 971 )
969 972 coreconfigitem('progress', 'disable',
970 973 default=False,
971 974 )
972 975 coreconfigitem('progress', 'estimateinterval',
973 976 default=60.0,
974 977 )
975 978 coreconfigitem('progress', 'format',
976 979 default=lambda: ['topic', 'bar', 'number', 'estimate'],
977 980 )
978 981 coreconfigitem('progress', 'refresh',
979 982 default=0.1,
980 983 )
981 984 coreconfigitem('progress', 'width',
982 985 default=dynamicdefault,
983 986 )
984 987 coreconfigitem('push', 'pushvars.server',
985 988 default=False,
986 989 )
987 990 coreconfigitem('storage', 'mmap-threshold',
988 991 default='1MB',
989 992 alias=[('experimental', 'mmapindexthreshold')],
990 993 )
991 994 coreconfigitem('storage', 'new-repo-backend',
992 995 default='revlogv1',
993 996 )
994 997 coreconfigitem('storage', 'revlog.optimize-delta-parent-choice',
995 998 default=True,
996 999 alias=[('format', 'aggressivemergedeltas')],
997 1000 )
998 1001 coreconfigitem('server', 'bookmarks-pushkey-compat',
999 1002 default=True,
1000 1003 )
1001 1004 coreconfigitem('server', 'bundle1',
1002 1005 default=True,
1003 1006 )
1004 1007 coreconfigitem('server', 'bundle1gd',
1005 1008 default=None,
1006 1009 )
1007 1010 coreconfigitem('server', 'bundle1.pull',
1008 1011 default=None,
1009 1012 )
1010 1013 coreconfigitem('server', 'bundle1gd.pull',
1011 1014 default=None,
1012 1015 )
1013 1016 coreconfigitem('server', 'bundle1.push',
1014 1017 default=None,
1015 1018 )
1016 1019 coreconfigitem('server', 'bundle1gd.push',
1017 1020 default=None,
1018 1021 )
1019 1022 coreconfigitem('server', 'bundle2.stream',
1020 1023 default=True,
1021 1024 alias=[('experimental', 'bundle2.stream')]
1022 1025 )
1023 1026 coreconfigitem('server', 'compressionengines',
1024 1027 default=list,
1025 1028 )
1026 1029 coreconfigitem('server', 'concurrent-push-mode',
1027 1030 default='strict',
1028 1031 )
1029 1032 coreconfigitem('server', 'disablefullbundle',
1030 1033 default=False,
1031 1034 )
1032 1035 coreconfigitem('server', 'maxhttpheaderlen',
1033 1036 default=1024,
1034 1037 )
1035 1038 coreconfigitem('server', 'pullbundle',
1036 1039 default=False,
1037 1040 )
1038 1041 coreconfigitem('server', 'preferuncompressed',
1039 1042 default=False,
1040 1043 )
1041 1044 coreconfigitem('server', 'streamunbundle',
1042 1045 default=False,
1043 1046 )
1044 1047 coreconfigitem('server', 'uncompressed',
1045 1048 default=True,
1046 1049 )
1047 1050 coreconfigitem('server', 'uncompressedallowsecret',
1048 1051 default=False,
1049 1052 )
1050 1053 coreconfigitem('server', 'validate',
1051 1054 default=False,
1052 1055 )
1053 1056 coreconfigitem('server', 'zliblevel',
1054 1057 default=-1,
1055 1058 )
1056 1059 coreconfigitem('server', 'zstdlevel',
1057 1060 default=3,
1058 1061 )
1059 1062 coreconfigitem('share', 'pool',
1060 1063 default=None,
1061 1064 )
1062 1065 coreconfigitem('share', 'poolnaming',
1063 1066 default='identity',
1064 1067 )
1065 1068 coreconfigitem('smtp', 'host',
1066 1069 default=None,
1067 1070 )
1068 1071 coreconfigitem('smtp', 'local_hostname',
1069 1072 default=None,
1070 1073 )
1071 1074 coreconfigitem('smtp', 'password',
1072 1075 default=None,
1073 1076 )
1074 1077 coreconfigitem('smtp', 'port',
1075 1078 default=dynamicdefault,
1076 1079 )
1077 1080 coreconfigitem('smtp', 'tls',
1078 1081 default='none',
1079 1082 )
1080 1083 coreconfigitem('smtp', 'username',
1081 1084 default=None,
1082 1085 )
1083 1086 coreconfigitem('sparse', 'missingwarning',
1084 1087 default=True,
1085 1088 )
1086 1089 coreconfigitem('subrepos', 'allowed',
1087 1090 default=dynamicdefault, # to make backporting simpler
1088 1091 )
1089 1092 coreconfigitem('subrepos', 'hg:allowed',
1090 1093 default=dynamicdefault,
1091 1094 )
1092 1095 coreconfigitem('subrepos', 'git:allowed',
1093 1096 default=dynamicdefault,
1094 1097 )
1095 1098 coreconfigitem('subrepos', 'svn:allowed',
1096 1099 default=dynamicdefault,
1097 1100 )
1098 1101 coreconfigitem('templates', '.*',
1099 1102 default=None,
1100 1103 generic=True,
1101 1104 )
1102 1105 coreconfigitem('trusted', 'groups',
1103 1106 default=list,
1104 1107 )
1105 1108 coreconfigitem('trusted', 'users',
1106 1109 default=list,
1107 1110 )
1108 1111 coreconfigitem('ui', '_usedassubrepo',
1109 1112 default=False,
1110 1113 )
1111 1114 coreconfigitem('ui', 'allowemptycommit',
1112 1115 default=False,
1113 1116 )
1114 1117 coreconfigitem('ui', 'archivemeta',
1115 1118 default=True,
1116 1119 )
1117 1120 coreconfigitem('ui', 'askusername',
1118 1121 default=False,
1119 1122 )
1120 1123 coreconfigitem('ui', 'clonebundlefallback',
1121 1124 default=False,
1122 1125 )
1123 1126 coreconfigitem('ui', 'clonebundleprefers',
1124 1127 default=list,
1125 1128 )
1126 1129 coreconfigitem('ui', 'clonebundles',
1127 1130 default=True,
1128 1131 )
1129 1132 coreconfigitem('ui', 'color',
1130 1133 default='auto',
1131 1134 )
1132 1135 coreconfigitem('ui', 'commitsubrepos',
1133 1136 default=False,
1134 1137 )
1135 1138 coreconfigitem('ui', 'debug',
1136 1139 default=False,
1137 1140 )
1138 1141 coreconfigitem('ui', 'debugger',
1139 1142 default=None,
1140 1143 )
1141 1144 coreconfigitem('ui', 'editor',
1142 1145 default=dynamicdefault,
1143 1146 )
1144 1147 coreconfigitem('ui', 'fallbackencoding',
1145 1148 default=None,
1146 1149 )
1147 1150 coreconfigitem('ui', 'forcecwd',
1148 1151 default=None,
1149 1152 )
1150 1153 coreconfigitem('ui', 'forcemerge',
1151 1154 default=None,
1152 1155 )
1153 1156 coreconfigitem('ui', 'formatdebug',
1154 1157 default=False,
1155 1158 )
1156 1159 coreconfigitem('ui', 'formatjson',
1157 1160 default=False,
1158 1161 )
1159 1162 coreconfigitem('ui', 'formatted',
1160 1163 default=None,
1161 1164 )
1162 1165 coreconfigitem('ui', 'graphnodetemplate',
1163 1166 default=None,
1164 1167 )
1165 1168 coreconfigitem('ui', 'history-editing-backup',
1166 1169 default=True,
1167 1170 )
1168 1171 coreconfigitem('ui', 'interactive',
1169 1172 default=None,
1170 1173 )
1171 1174 coreconfigitem('ui', 'interface',
1172 1175 default=None,
1173 1176 )
1174 1177 coreconfigitem('ui', 'interface.chunkselector',
1175 1178 default=None,
1176 1179 )
1177 1180 coreconfigitem('ui', 'large-file-limit',
1178 1181 default=10000000,
1179 1182 )
1180 1183 coreconfigitem('ui', 'logblockedtimes',
1181 1184 default=False,
1182 1185 )
1183 1186 coreconfigitem('ui', 'logtemplate',
1184 1187 default=None,
1185 1188 )
1186 1189 coreconfigitem('ui', 'merge',
1187 1190 default=None,
1188 1191 )
1189 1192 coreconfigitem('ui', 'mergemarkers',
1190 1193 default='basic',
1191 1194 )
1192 1195 coreconfigitem('ui', 'mergemarkertemplate',
1193 1196 default=('{node|short} '
1194 1197 '{ifeq(tags, "tip", "", '
1195 1198 'ifeq(tags, "", "", "{tags} "))}'
1196 1199 '{if(bookmarks, "{bookmarks} ")}'
1197 1200 '{ifeq(branch, "default", "", "{branch} ")}'
1198 1201 '- {author|user}: {desc|firstline}')
1199 1202 )
1200 1203 coreconfigitem('ui', 'message-output',
1201 1204 default='stdio',
1202 1205 )
1203 1206 coreconfigitem('ui', 'nontty',
1204 1207 default=False,
1205 1208 )
1206 1209 coreconfigitem('ui', 'origbackuppath',
1207 1210 default=None,
1208 1211 )
1209 1212 coreconfigitem('ui', 'paginate',
1210 1213 default=True,
1211 1214 )
1212 1215 coreconfigitem('ui', 'patch',
1213 1216 default=None,
1214 1217 )
1215 1218 coreconfigitem('ui', 'pre-merge-tool-output-template',
1216 1219 default=None,
1217 1220 )
1218 1221 coreconfigitem('ui', 'portablefilenames',
1219 1222 default='warn',
1220 1223 )
1221 1224 coreconfigitem('ui', 'promptecho',
1222 1225 default=False,
1223 1226 )
1224 1227 coreconfigitem('ui', 'quiet',
1225 1228 default=False,
1226 1229 )
1227 1230 coreconfigitem('ui', 'quietbookmarkmove',
1228 1231 default=False,
1229 1232 )
1230 1233 coreconfigitem('ui', 'remotecmd',
1231 1234 default='hg',
1232 1235 )
1233 1236 coreconfigitem('ui', 'report_untrusted',
1234 1237 default=True,
1235 1238 )
1236 1239 coreconfigitem('ui', 'rollback',
1237 1240 default=True,
1238 1241 )
1239 1242 coreconfigitem('ui', 'signal-safe-lock',
1240 1243 default=True,
1241 1244 )
1242 1245 coreconfigitem('ui', 'slash',
1243 1246 default=False,
1244 1247 )
1245 1248 coreconfigitem('ui', 'ssh',
1246 1249 default='ssh',
1247 1250 )
1248 1251 coreconfigitem('ui', 'ssherrorhint',
1249 1252 default=None,
1250 1253 )
1251 1254 coreconfigitem('ui', 'statuscopies',
1252 1255 default=False,
1253 1256 )
1254 1257 coreconfigitem('ui', 'strict',
1255 1258 default=False,
1256 1259 )
1257 1260 coreconfigitem('ui', 'style',
1258 1261 default='',
1259 1262 )
1260 1263 coreconfigitem('ui', 'supportcontact',
1261 1264 default=None,
1262 1265 )
1263 1266 coreconfigitem('ui', 'textwidth',
1264 1267 default=78,
1265 1268 )
1266 1269 coreconfigitem('ui', 'timeout',
1267 1270 default='600',
1268 1271 )
1269 1272 coreconfigitem('ui', 'timeout.warn',
1270 1273 default=0,
1271 1274 )
1272 1275 coreconfigitem('ui', 'traceback',
1273 1276 default=False,
1274 1277 )
1275 1278 coreconfigitem('ui', 'tweakdefaults',
1276 1279 default=False,
1277 1280 )
1278 1281 coreconfigitem('ui', 'username',
1279 1282 alias=[('ui', 'user')]
1280 1283 )
1281 1284 coreconfigitem('ui', 'verbose',
1282 1285 default=False,
1283 1286 )
1284 1287 coreconfigitem('verify', 'skipflags',
1285 1288 default=None,
1286 1289 )
1287 1290 coreconfigitem('web', 'allowbz2',
1288 1291 default=False,
1289 1292 )
1290 1293 coreconfigitem('web', 'allowgz',
1291 1294 default=False,
1292 1295 )
1293 1296 coreconfigitem('web', 'allow-pull',
1294 1297 alias=[('web', 'allowpull')],
1295 1298 default=True,
1296 1299 )
1297 1300 coreconfigitem('web', 'allow-push',
1298 1301 alias=[('web', 'allow_push')],
1299 1302 default=list,
1300 1303 )
1301 1304 coreconfigitem('web', 'allowzip',
1302 1305 default=False,
1303 1306 )
1304 1307 coreconfigitem('web', 'archivesubrepos',
1305 1308 default=False,
1306 1309 )
1307 1310 coreconfigitem('web', 'cache',
1308 1311 default=True,
1309 1312 )
1310 1313 coreconfigitem('web', 'comparisoncontext',
1311 1314 default=5,
1312 1315 )
1313 1316 coreconfigitem('web', 'contact',
1314 1317 default=None,
1315 1318 )
1316 1319 coreconfigitem('web', 'deny_push',
1317 1320 default=list,
1318 1321 )
1319 1322 coreconfigitem('web', 'guessmime',
1320 1323 default=False,
1321 1324 )
1322 1325 coreconfigitem('web', 'hidden',
1323 1326 default=False,
1324 1327 )
1325 1328 coreconfigitem('web', 'labels',
1326 1329 default=list,
1327 1330 )
1328 1331 coreconfigitem('web', 'logoimg',
1329 1332 default='hglogo.png',
1330 1333 )
1331 1334 coreconfigitem('web', 'logourl',
1332 1335 default='https://mercurial-scm.org/',
1333 1336 )
1334 1337 coreconfigitem('web', 'accesslog',
1335 1338 default='-',
1336 1339 )
1337 1340 coreconfigitem('web', 'address',
1338 1341 default='',
1339 1342 )
1340 1343 coreconfigitem('web', 'allow-archive',
1341 1344 alias=[('web', 'allow_archive')],
1342 1345 default=list,
1343 1346 )
1344 1347 coreconfigitem('web', 'allow_read',
1345 1348 default=list,
1346 1349 )
1347 1350 coreconfigitem('web', 'baseurl',
1348 1351 default=None,
1349 1352 )
1350 1353 coreconfigitem('web', 'cacerts',
1351 1354 default=None,
1352 1355 )
1353 1356 coreconfigitem('web', 'certificate',
1354 1357 default=None,
1355 1358 )
1356 1359 coreconfigitem('web', 'collapse',
1357 1360 default=False,
1358 1361 )
1359 1362 coreconfigitem('web', 'csp',
1360 1363 default=None,
1361 1364 )
1362 1365 coreconfigitem('web', 'deny_read',
1363 1366 default=list,
1364 1367 )
1365 1368 coreconfigitem('web', 'descend',
1366 1369 default=True,
1367 1370 )
1368 1371 coreconfigitem('web', 'description',
1369 1372 default="",
1370 1373 )
1371 1374 coreconfigitem('web', 'encoding',
1372 1375 default=lambda: encoding.encoding,
1373 1376 )
1374 1377 coreconfigitem('web', 'errorlog',
1375 1378 default='-',
1376 1379 )
1377 1380 coreconfigitem('web', 'ipv6',
1378 1381 default=False,
1379 1382 )
1380 1383 coreconfigitem('web', 'maxchanges',
1381 1384 default=10,
1382 1385 )
1383 1386 coreconfigitem('web', 'maxfiles',
1384 1387 default=10,
1385 1388 )
1386 1389 coreconfigitem('web', 'maxshortchanges',
1387 1390 default=60,
1388 1391 )
1389 1392 coreconfigitem('web', 'motd',
1390 1393 default='',
1391 1394 )
1392 1395 coreconfigitem('web', 'name',
1393 1396 default=dynamicdefault,
1394 1397 )
1395 1398 coreconfigitem('web', 'port',
1396 1399 default=8000,
1397 1400 )
1398 1401 coreconfigitem('web', 'prefix',
1399 1402 default='',
1400 1403 )
1401 1404 coreconfigitem('web', 'push_ssl',
1402 1405 default=True,
1403 1406 )
1404 1407 coreconfigitem('web', 'refreshinterval',
1405 1408 default=20,
1406 1409 )
1407 1410 coreconfigitem('web', 'server-header',
1408 1411 default=None,
1409 1412 )
1410 1413 coreconfigitem('web', 'static',
1411 1414 default=None,
1412 1415 )
1413 1416 coreconfigitem('web', 'staticurl',
1414 1417 default=None,
1415 1418 )
1416 1419 coreconfigitem('web', 'stripes',
1417 1420 default=1,
1418 1421 )
1419 1422 coreconfigitem('web', 'style',
1420 1423 default='paper',
1421 1424 )
1422 1425 coreconfigitem('web', 'templates',
1423 1426 default=None,
1424 1427 )
1425 1428 coreconfigitem('web', 'view',
1426 1429 default='served',
1427 1430 )
1428 1431 coreconfigitem('worker', 'backgroundclose',
1429 1432 default=dynamicdefault,
1430 1433 )
1431 1434 # Windows defaults to a limit of 512 open files. A buffer of 128
1432 1435 # should give us enough headway.
1433 1436 coreconfigitem('worker', 'backgroundclosemaxqueue',
1434 1437 default=384,
1435 1438 )
1436 1439 coreconfigitem('worker', 'backgroundcloseminfilecount',
1437 1440 default=2048,
1438 1441 )
1439 1442 coreconfigitem('worker', 'backgroundclosethreadcount',
1440 1443 default=4,
1441 1444 )
1442 1445 coreconfigitem('worker', 'enabled',
1443 1446 default=True,
1444 1447 )
1445 1448 coreconfigitem('worker', 'numcpus',
1446 1449 default=None,
1447 1450 )
1448 1451
1449 1452 # Rebase related configuration moved to core because other extension are doing
1450 1453 # strange things. For example, shelve import the extensions to reuse some bit
1451 1454 # without formally loading it.
1452 1455 coreconfigitem('commands', 'rebase.requiredest',
1453 1456 default=False,
1454 1457 )
1455 1458 coreconfigitem('experimental', 'rebaseskipobsolete',
1456 1459 default=True,
1457 1460 )
1458 1461 coreconfigitem('rebase', 'singletransaction',
1459 1462 default=False,
1460 1463 )
1461 1464 coreconfigitem('rebase', 'experimental.inmemory',
1462 1465 default=False,
1463 1466 )
@@ -1,242 +1,331 b''
1 1 #require chg
2 2
3 3 $ mkdir log
4 $ cp $HGRCPATH $HGRCPATH.unconfigured
4 5 $ cat <<'EOF' >> $HGRCPATH
5 6 > [cmdserver]
6 7 > log = $TESTTMP/log/server.log
7 8 > max-log-files = 1
8 9 > max-log-size = 10 kB
9 10 > EOF
10 11 $ cp $HGRCPATH $HGRCPATH.orig
11 12
12 13 $ filterlog () {
13 14 > sed -e 's!^[0-9/]* [0-9:]* ([0-9]*)>!YYYY/MM/DD HH:MM:SS (PID)>!' \
14 15 > -e 's!\(setprocname\|received fds\|setenv\): .*!\1: ...!' \
15 16 > -e 's!\(confighash\|mtimehash\) = [0-9a-f]*!\1 = ...!g' \
17 > -e 's!\(in \)[0-9.]*s\b!\1 ...s!g' \
16 18 > -e 's!\(pid\)=[0-9]*!\1=...!g' \
17 19 > -e 's!\(/server-\)[0-9a-f]*!\1...!g'
18 20 > }
19 21
20 22 init repo
21 23
22 24 $ chg init foo
23 25 $ cd foo
24 26
25 27 ill-formed config
26 28
27 29 $ chg status
28 30 $ echo '=brokenconfig' >> $HGRCPATH
29 31 $ chg status
30 32 hg: parse error at * (glob)
31 33 [255]
32 34
33 35 $ cp $HGRCPATH.orig $HGRCPATH
34 36
35 37 long socket path
36 38
37 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
38 40 $ mkdir -p $sockpath
39 41 $ bakchgsockname=$CHGSOCKNAME
40 42 $ CHGSOCKNAME=$sockpath/server
41 43 $ export CHGSOCKNAME
42 44 $ chg root
43 45 $TESTTMP/foo
44 46 $ rm -rf $sockpath
45 47 $ CHGSOCKNAME=$bakchgsockname
46 48 $ export CHGSOCKNAME
47 49
48 50 $ cd ..
49 51
50 52 editor
51 53 ------
52 54
53 55 $ cat >> pushbuffer.py <<EOF
54 56 > def reposetup(ui, repo):
55 57 > repo.ui.pushbuffer(subproc=True)
56 58 > EOF
57 59
58 60 $ chg init editor
59 61 $ cd editor
60 62
61 63 by default, system() should be redirected to the client:
62 64
63 65 $ touch foo
64 66 $ CHGDEBUG= HGEDITOR=cat chg ci -Am channeled --edit 2>&1 \
65 67 > | egrep "HG:|run 'cat"
66 68 chg: debug: * run 'cat "*"' at '$TESTTMP/editor' (glob)
67 69 HG: Enter commit message. Lines beginning with 'HG:' are removed.
68 70 HG: Leave message empty to abort commit.
69 71 HG: --
70 72 HG: user: test
71 73 HG: branch 'default'
72 74 HG: added foo
73 75
74 76 but no redirection should be made if output is captured:
75 77
76 78 $ touch bar
77 79 $ CHGDEBUG= HGEDITOR=cat chg ci -Am bufferred --edit \
78 80 > --config extensions.pushbuffer="$TESTTMP/pushbuffer.py" 2>&1 \
79 81 > | egrep "HG:|run 'cat"
80 82 [1]
81 83
82 84 check that commit commands succeeded:
83 85
84 86 $ hg log -T '{rev}:{desc}\n'
85 87 1:bufferred
86 88 0:channeled
87 89
88 90 $ cd ..
89 91
90 92 pager
91 93 -----
92 94
93 95 $ cat >> fakepager.py <<EOF
94 96 > import sys
95 97 > for line in sys.stdin:
96 98 > sys.stdout.write('paged! %r\n' % line)
97 99 > EOF
98 100
99 101 enable pager extension globally, but spawns the master server with no tty:
100 102
101 103 $ chg init pager
102 104 $ cd pager
103 105 $ cat >> $HGRCPATH <<EOF
104 106 > [extensions]
105 107 > pager =
106 108 > [pager]
107 109 > pager = "$PYTHON" $TESTTMP/fakepager.py
108 110 > EOF
109 111 $ chg version > /dev/null
110 112 $ touch foo
111 113 $ chg ci -qAm foo
112 114
113 115 pager should be enabled if the attached client has a tty:
114 116
115 117 $ chg log -l1 -q --config ui.formatted=True
116 118 paged! '0:1f7b0de80e11\n'
117 119 $ chg log -l1 -q --config ui.formatted=False
118 120 0:1f7b0de80e11
119 121
120 122 chg waits for pager if runcommand raises
121 123
122 124 $ cat > $TESTTMP/crash.py <<EOF
123 125 > from mercurial import registrar
124 126 > cmdtable = {}
125 127 > command = registrar.command(cmdtable)
126 128 > @command(b'crash')
127 129 > def pagercrash(ui, repo, *pats, **opts):
128 130 > ui.write('going to crash\n')
129 131 > raise Exception('.')
130 132 > EOF
131 133
132 134 $ cat > $TESTTMP/fakepager.py <<EOF
133 135 > from __future__ import absolute_import
134 136 > import sys
135 137 > import time
136 138 > for line in iter(sys.stdin.readline, ''):
137 139 > if 'crash' in line: # only interested in lines containing 'crash'
138 140 > # if chg exits when pager is sleeping (incorrectly), the output
139 141 > # will be captured by the next test case
140 142 > time.sleep(1)
141 143 > sys.stdout.write('crash-pager: %s' % line)
142 144 > EOF
143 145
144 146 $ cat >> .hg/hgrc <<EOF
145 147 > [extensions]
146 148 > crash = $TESTTMP/crash.py
147 149 > EOF
148 150
149 151 $ chg crash --pager=on --config ui.formatted=True 2>/dev/null
150 152 crash-pager: going to crash
151 153 [255]
152 154
153 155 $ cd ..
154 156
155 157 server lifecycle
156 158 ----------------
157 159
158 160 chg server should be restarted on code change, and old server will shut down
159 161 automatically. In this test, we use the following time parameters:
160 162
161 163 - "sleep 1" to make mtime different
162 164 - "sleep 2" to notice mtime change (polling interval is 1 sec)
163 165
164 166 set up repository with an extension:
165 167
166 168 $ chg init extreload
167 169 $ cd extreload
168 170 $ touch dummyext.py
169 171 $ cat <<EOF >> .hg/hgrc
170 172 > [extensions]
171 173 > dummyext = dummyext.py
172 174 > EOF
173 175
174 176 isolate socket directory for stable result:
175 177
176 178 $ OLDCHGSOCKNAME=$CHGSOCKNAME
177 179 $ mkdir chgsock
178 180 $ CHGSOCKNAME=`pwd`/chgsock/server
179 181
180 182 warm up server:
181 183
182 184 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
183 185 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
184 186
185 187 new server should be started if extension modified:
186 188
187 189 $ sleep 1
188 190 $ touch dummyext.py
189 191 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
190 192 chg: debug: * instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
191 193 chg: debug: * instruction: reconnect (glob)
192 194 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
193 195
194 196 old server will shut down, while new server should still be reachable:
195 197
196 198 $ sleep 2
197 199 $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)
198 200
199 201 socket file should never be unlinked by old server:
200 202 (simulates unowned socket by updating mtime, which makes sure server exits
201 203 at polling cycle)
202 204
203 205 $ ls chgsock/server-*
204 206 chgsock/server-* (glob)
205 207 $ touch chgsock/server-*
206 208 $ sleep 2
207 209 $ ls chgsock/server-*
208 210 chgsock/server-* (glob)
209 211
210 212 since no server is reachable from socket file, new server should be started:
211 213 (this test makes sure that old server shut down automatically)
212 214
213 215 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
214 216 chg: debug: * start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
215 217
216 218 shut down servers and restore environment:
217 219
218 220 $ rm -R chgsock
219 221 $ sleep 2
220 222 $ CHGSOCKNAME=$OLDCHGSOCKNAME
221 223 $ cd ..
222 224
223 225 check that server events are recorded:
224 226
225 227 $ ls log
226 228 server.log
227 229 server.log.1
228 230
229 231 print only the last 10 lines, since we aren't sure how many records are
230 232 preserved:
231 233
232 234 $ cat log/server.log.1 log/server.log | tail -10 | filterlog
235 YYYY/MM/DD HH:MM:SS (PID)> forked worker process (pid=...)
233 236 YYYY/MM/DD HH:MM:SS (PID)> setprocname: ...
234 237 YYYY/MM/DD HH:MM:SS (PID)> received fds: ...
235 238 YYYY/MM/DD HH:MM:SS (PID)> chdir to '$TESTTMP/extreload'
236 239 YYYY/MM/DD HH:MM:SS (PID)> setumask 18
237 240 YYYY/MM/DD HH:MM:SS (PID)> setenv: ...
238 241 YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ...
239 242 YYYY/MM/DD HH:MM:SS (PID)> validate: []
240 YYYY/MM/DD HH:MM:SS (PID)> repository: $TESTTMP/extreload
241 243 YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...)
242 244 YYYY/MM/DD HH:MM:SS (PID)> $TESTTMP/extreload/chgsock/server-... is not owned, exiting.
245
246 repository cache
247 ----------------
248
249 $ rm log/server.log*
250 $ cp $HGRCPATH.unconfigured $HGRCPATH
251 $ cat <<'EOF' >> $HGRCPATH
252 > [cmdserver]
253 > log = $TESTTMP/log/server.log
254 > max-repo-cache = 1
255 > track-log = command, repocache
256 > EOF
257
258 isolate socket directory for stable result:
259
260 $ OLDCHGSOCKNAME=$CHGSOCKNAME
261 $ mkdir chgsock
262 $ CHGSOCKNAME=`pwd`/chgsock/server
263
264 create empty repo and cache it:
265
266 $ hg init cached
267 $ hg id -R cached
268 000000000000 tip
269 $ sleep 1
270
271 modify repo (and cache will be invalidated):
272
273 $ touch cached/a
274 $ hg ci -R cached -Am 'add a'
275 adding a
276 $ sleep 1
277
278 read cached repo:
279
280 $ hg log -R cached
281 changeset: 0:ac82d8b1f7c4
282 tag: tip
283 user: test
284 date: Thu Jan 01 00:00:00 1970 +0000
285 summary: add a
286
287 $ sleep 1
288
289 discard cached from LRU cache:
290
291 $ hg clone cached cached2
292 updating to branch default
293 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 $ hg id -R cached2
295 ac82d8b1f7c4 tip
296 $ sleep 1
297
298 read uncached repo:
299
300 $ hg log -R cached
301 changeset: 0:ac82d8b1f7c4
302 tag: tip
303 user: test
304 date: Thu Jan 01 00:00:00 1970 +0000
305 summary: add a
306
307 $ sleep 1
308
309 shut down servers and restore environment:
310
311 $ rm -R chgsock
312 $ sleep 2
313 $ CHGSOCKNAME=$OLDCHGSOCKNAME
314
315 check server log:
316
317 $ cat log/server.log | filterlog
318 YYYY/MM/DD HH:MM:SS (PID)> init cached
319 YYYY/MM/DD HH:MM:SS (PID)> id -R cached
320 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
321 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
322 YYYY/MM/DD HH:MM:SS (PID)> ci -R cached -Am 'add a'
323 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
324 YYYY/MM/DD HH:MM:SS (PID)> repo from cache: $TESTTMP/cached
325 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
326 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
327 YYYY/MM/DD HH:MM:SS (PID)> clone cached cached2
328 YYYY/MM/DD HH:MM:SS (PID)> id -R cached2
329 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached2 (in ...s)
330 YYYY/MM/DD HH:MM:SS (PID)> log -R cached
331 YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s)
General Comments 0
You need to be logged in to leave comments. Login now