##// END OF EJS Templates
ui: add option to timestamp status and diagnostic messages...
Joerg Sonnenberger -
r45564:24b1a8eb default
parent child Browse files
Show More
@@ -1,1572 +1,1575 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
19 19 def loadconfigtable(ui, extname, configtable):
20 20 """update config item known to the ui with the extension ones"""
21 21 for section, items in sorted(configtable.items()):
22 22 knownitems = ui._knownconfig.setdefault(section, itemregister())
23 23 knownkeys = set(knownitems)
24 24 newkeys = set(items)
25 25 for key in sorted(knownkeys & newkeys):
26 26 msg = b"extension '%s' overwrite config item '%s.%s'"
27 27 msg %= (extname, section, key)
28 28 ui.develwarn(msg, config=b'warn-config')
29 29
30 30 knownitems.update(items)
31 31
32 32
33 33 class configitem(object):
34 34 """represent a known config item
35 35
36 36 :section: the official config section where to find this item,
37 37 :name: the official name within the section,
38 38 :default: default value for this item,
39 39 :alias: optional list of tuples as alternatives,
40 40 :generic: this is a generic definition, match name using regular expression.
41 41 """
42 42
43 43 def __init__(
44 44 self,
45 45 section,
46 46 name,
47 47 default=None,
48 48 alias=(),
49 49 generic=False,
50 50 priority=0,
51 51 experimental=False,
52 52 ):
53 53 self.section = section
54 54 self.name = name
55 55 self.default = default
56 56 self.alias = list(alias)
57 57 self.generic = generic
58 58 self.priority = priority
59 59 self.experimental = experimental
60 60 self._re = None
61 61 if generic:
62 62 self._re = re.compile(self.name)
63 63
64 64
65 65 class itemregister(dict):
66 66 """A specialized dictionary that can handle wild-card selection"""
67 67
68 68 def __init__(self):
69 69 super(itemregister, self).__init__()
70 70 self._generics = set()
71 71
72 72 def update(self, other):
73 73 super(itemregister, self).update(other)
74 74 self._generics.update(other._generics)
75 75
76 76 def __setitem__(self, key, item):
77 77 super(itemregister, self).__setitem__(key, item)
78 78 if item.generic:
79 79 self._generics.add(item)
80 80
81 81 def get(self, key):
82 82 baseitem = super(itemregister, self).get(key)
83 83 if baseitem is not None and not baseitem.generic:
84 84 return baseitem
85 85
86 86 # search for a matching generic item
87 87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
88 88 for item in generics:
89 89 # we use 'match' instead of 'search' to make the matching simpler
90 90 # for people unfamiliar with regular expression. Having the match
91 91 # rooted to the start of the string will produce less surprising
92 92 # result for user writing simple regex for sub-attribute.
93 93 #
94 94 # For example using "color\..*" match produces an unsurprising
95 95 # result, while using search could suddenly match apparently
96 96 # unrelated configuration that happens to contains "color."
97 97 # anywhere. This is a tradeoff where we favor requiring ".*" on
98 98 # some match to avoid the need to prefix most pattern with "^".
99 99 # The "^" seems more error prone.
100 100 if item._re.match(key):
101 101 return item
102 102
103 103 return None
104 104
105 105
106 106 coreitems = {}
107 107
108 108
109 109 def _register(configtable, *args, **kwargs):
110 110 item = configitem(*args, **kwargs)
111 111 section = configtable.setdefault(item.section, itemregister())
112 112 if item.name in section:
113 113 msg = b"duplicated config item registration for '%s.%s'"
114 114 raise error.ProgrammingError(msg % (item.section, item.name))
115 115 section[item.name] = item
116 116
117 117
118 118 # special value for case where the default is derived from other values
119 119 dynamicdefault = object()
120 120
121 121 # Registering actual config items
122 122
123 123
124 124 def getitemregister(configtable):
125 125 f = functools.partial(_register, configtable)
126 126 # export pseudo enum as configitem.*
127 127 f.dynamicdefault = dynamicdefault
128 128 return f
129 129
130 130
131 131 coreconfigitem = getitemregister(coreitems)
132 132
133 133
134 134 def _registerdiffopts(section, configprefix=b''):
135 135 coreconfigitem(
136 136 section, configprefix + b'nodates', default=False,
137 137 )
138 138 coreconfigitem(
139 139 section, configprefix + b'showfunc', default=False,
140 140 )
141 141 coreconfigitem(
142 142 section, configprefix + b'unified', default=None,
143 143 )
144 144 coreconfigitem(
145 145 section, configprefix + b'git', default=False,
146 146 )
147 147 coreconfigitem(
148 148 section, configprefix + b'ignorews', default=False,
149 149 )
150 150 coreconfigitem(
151 151 section, configprefix + b'ignorewsamount', default=False,
152 152 )
153 153 coreconfigitem(
154 154 section, configprefix + b'ignoreblanklines', default=False,
155 155 )
156 156 coreconfigitem(
157 157 section, configprefix + b'ignorewseol', default=False,
158 158 )
159 159 coreconfigitem(
160 160 section, configprefix + b'nobinary', default=False,
161 161 )
162 162 coreconfigitem(
163 163 section, configprefix + b'noprefix', default=False,
164 164 )
165 165 coreconfigitem(
166 166 section, configprefix + b'word-diff', default=False,
167 167 )
168 168
169 169
170 170 coreconfigitem(
171 171 b'alias', b'.*', default=dynamicdefault, generic=True,
172 172 )
173 173 coreconfigitem(
174 174 b'auth', b'cookiefile', default=None,
175 175 )
176 176 _registerdiffopts(section=b'annotate')
177 177 # bookmarks.pushing: internal hack for discovery
178 178 coreconfigitem(
179 179 b'bookmarks', b'pushing', default=list,
180 180 )
181 181 # bundle.mainreporoot: internal hack for bundlerepo
182 182 coreconfigitem(
183 183 b'bundle', b'mainreporoot', default=b'',
184 184 )
185 185 coreconfigitem(
186 186 b'censor', b'policy', default=b'abort', experimental=True,
187 187 )
188 188 coreconfigitem(
189 189 b'chgserver', b'idletimeout', default=3600,
190 190 )
191 191 coreconfigitem(
192 192 b'chgserver', b'skiphash', default=False,
193 193 )
194 194 coreconfigitem(
195 195 b'cmdserver', b'log', default=None,
196 196 )
197 197 coreconfigitem(
198 198 b'cmdserver', b'max-log-files', default=7,
199 199 )
200 200 coreconfigitem(
201 201 b'cmdserver', b'max-log-size', default=b'1 MB',
202 202 )
203 203 coreconfigitem(
204 204 b'cmdserver', b'max-repo-cache', default=0, experimental=True,
205 205 )
206 206 coreconfigitem(
207 207 b'cmdserver', b'message-encodings', default=list, experimental=True,
208 208 )
209 209 coreconfigitem(
210 210 b'cmdserver',
211 211 b'track-log',
212 212 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
213 213 )
214 214 coreconfigitem(
215 215 b'color', b'.*', default=None, generic=True,
216 216 )
217 217 coreconfigitem(
218 218 b'color', b'mode', default=b'auto',
219 219 )
220 220 coreconfigitem(
221 221 b'color', b'pagermode', default=dynamicdefault,
222 222 )
223 223 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
224 224 coreconfigitem(
225 225 b'commands', b'commit.post-status', default=False,
226 226 )
227 227 coreconfigitem(
228 228 b'commands', b'grep.all-files', default=False, experimental=True,
229 229 )
230 230 coreconfigitem(
231 231 b'commands', b'merge.require-rev', default=False,
232 232 )
233 233 coreconfigitem(
234 234 b'commands', b'push.require-revs', default=False,
235 235 )
236 236 coreconfigitem(
237 237 b'commands', b'resolve.confirm', default=False,
238 238 )
239 239 coreconfigitem(
240 240 b'commands', b'resolve.explicit-re-merge', default=False,
241 241 )
242 242 coreconfigitem(
243 243 b'commands', b'resolve.mark-check', default=b'none',
244 244 )
245 245 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
246 246 coreconfigitem(
247 247 b'commands', b'show.aliasprefix', default=list,
248 248 )
249 249 coreconfigitem(
250 250 b'commands', b'status.relative', default=False,
251 251 )
252 252 coreconfigitem(
253 253 b'commands', b'status.skipstates', default=[], experimental=True,
254 254 )
255 255 coreconfigitem(
256 256 b'commands', b'status.terse', default=b'',
257 257 )
258 258 coreconfigitem(
259 259 b'commands', b'status.verbose', default=False,
260 260 )
261 261 coreconfigitem(
262 262 b'commands', b'update.check', default=None,
263 263 )
264 264 coreconfigitem(
265 265 b'commands', b'update.requiredest', default=False,
266 266 )
267 267 coreconfigitem(
268 268 b'committemplate', b'.*', default=None, generic=True,
269 269 )
270 270 coreconfigitem(
271 271 b'convert', b'bzr.saverev', default=True,
272 272 )
273 273 coreconfigitem(
274 274 b'convert', b'cvsps.cache', default=True,
275 275 )
276 276 coreconfigitem(
277 277 b'convert', b'cvsps.fuzz', default=60,
278 278 )
279 279 coreconfigitem(
280 280 b'convert', b'cvsps.logencoding', default=None,
281 281 )
282 282 coreconfigitem(
283 283 b'convert', b'cvsps.mergefrom', default=None,
284 284 )
285 285 coreconfigitem(
286 286 b'convert', b'cvsps.mergeto', default=None,
287 287 )
288 288 coreconfigitem(
289 289 b'convert', b'git.committeractions', default=lambda: [b'messagedifferent'],
290 290 )
291 291 coreconfigitem(
292 292 b'convert', b'git.extrakeys', default=list,
293 293 )
294 294 coreconfigitem(
295 295 b'convert', b'git.findcopiesharder', default=False,
296 296 )
297 297 coreconfigitem(
298 298 b'convert', b'git.remoteprefix', default=b'remote',
299 299 )
300 300 coreconfigitem(
301 301 b'convert', b'git.renamelimit', default=400,
302 302 )
303 303 coreconfigitem(
304 304 b'convert', b'git.saverev', default=True,
305 305 )
306 306 coreconfigitem(
307 307 b'convert', b'git.similarity', default=50,
308 308 )
309 309 coreconfigitem(
310 310 b'convert', b'git.skipsubmodules', default=False,
311 311 )
312 312 coreconfigitem(
313 313 b'convert', b'hg.clonebranches', default=False,
314 314 )
315 315 coreconfigitem(
316 316 b'convert', b'hg.ignoreerrors', default=False,
317 317 )
318 318 coreconfigitem(
319 319 b'convert', b'hg.preserve-hash', default=False,
320 320 )
321 321 coreconfigitem(
322 322 b'convert', b'hg.revs', default=None,
323 323 )
324 324 coreconfigitem(
325 325 b'convert', b'hg.saverev', default=False,
326 326 )
327 327 coreconfigitem(
328 328 b'convert', b'hg.sourcename', default=None,
329 329 )
330 330 coreconfigitem(
331 331 b'convert', b'hg.startrev', default=None,
332 332 )
333 333 coreconfigitem(
334 334 b'convert', b'hg.tagsbranch', default=b'default',
335 335 )
336 336 coreconfigitem(
337 337 b'convert', b'hg.usebranchnames', default=True,
338 338 )
339 339 coreconfigitem(
340 340 b'convert', b'ignoreancestorcheck', default=False, experimental=True,
341 341 )
342 342 coreconfigitem(
343 343 b'convert', b'localtimezone', default=False,
344 344 )
345 345 coreconfigitem(
346 346 b'convert', b'p4.encoding', default=dynamicdefault,
347 347 )
348 348 coreconfigitem(
349 349 b'convert', b'p4.startrev', default=0,
350 350 )
351 351 coreconfigitem(
352 352 b'convert', b'skiptags', default=False,
353 353 )
354 354 coreconfigitem(
355 355 b'convert', b'svn.debugsvnlog', default=True,
356 356 )
357 357 coreconfigitem(
358 358 b'convert', b'svn.trunk', default=None,
359 359 )
360 360 coreconfigitem(
361 361 b'convert', b'svn.tags', default=None,
362 362 )
363 363 coreconfigitem(
364 364 b'convert', b'svn.branches', default=None,
365 365 )
366 366 coreconfigitem(
367 367 b'convert', b'svn.startrev', default=0,
368 368 )
369 369 coreconfigitem(
370 370 b'debug', b'dirstate.delaywrite', default=0,
371 371 )
372 372 coreconfigitem(
373 373 b'defaults', b'.*', default=None, generic=True,
374 374 )
375 375 coreconfigitem(
376 376 b'devel', b'all-warnings', default=False,
377 377 )
378 378 coreconfigitem(
379 379 b'devel', b'bundle2.debug', default=False,
380 380 )
381 381 coreconfigitem(
382 382 b'devel', b'bundle.delta', default=b'',
383 383 )
384 384 coreconfigitem(
385 385 b'devel', b'cache-vfs', default=None,
386 386 )
387 387 coreconfigitem(
388 388 b'devel', b'check-locks', default=False,
389 389 )
390 390 coreconfigitem(
391 391 b'devel', b'check-relroot', default=False,
392 392 )
393 393 coreconfigitem(
394 394 b'devel', b'default-date', default=None,
395 395 )
396 396 coreconfigitem(
397 397 b'devel', b'deprec-warn', default=False,
398 398 )
399 399 coreconfigitem(
400 400 b'devel', b'disableloaddefaultcerts', default=False,
401 401 )
402 402 coreconfigitem(
403 403 b'devel', b'warn-empty-changegroup', default=False,
404 404 )
405 405 coreconfigitem(
406 406 b'devel', b'legacy.exchange', default=list,
407 407 )
408 408 coreconfigitem(
409 409 b'devel', b'persistent-nodemap', default=False,
410 410 )
411 411 coreconfigitem(
412 412 b'devel', b'servercafile', default=b'',
413 413 )
414 414 coreconfigitem(
415 415 b'devel', b'serverexactprotocol', default=b'',
416 416 )
417 417 coreconfigitem(
418 418 b'devel', b'serverrequirecert', default=False,
419 419 )
420 420 coreconfigitem(
421 421 b'devel', b'strip-obsmarkers', default=True,
422 422 )
423 423 coreconfigitem(
424 424 b'devel', b'warn-config', default=None,
425 425 )
426 426 coreconfigitem(
427 427 b'devel', b'warn-config-default', default=None,
428 428 )
429 429 coreconfigitem(
430 430 b'devel', b'user.obsmarker', default=None,
431 431 )
432 432 coreconfigitem(
433 433 b'devel', b'warn-config-unknown', default=None,
434 434 )
435 435 coreconfigitem(
436 436 b'devel', b'debug.copies', default=False,
437 437 )
438 438 coreconfigitem(
439 439 b'devel', b'debug.extensions', default=False,
440 440 )
441 441 coreconfigitem(
442 442 b'devel', b'debug.repo-filters', default=False,
443 443 )
444 444 coreconfigitem(
445 445 b'devel', b'debug.peer-request', default=False,
446 446 )
447 447 coreconfigitem(
448 448 b'devel', b'discovery.randomize', default=True,
449 449 )
450 450 _registerdiffopts(section=b'diff')
451 451 coreconfigitem(
452 452 b'email', b'bcc', default=None,
453 453 )
454 454 coreconfigitem(
455 455 b'email', b'cc', default=None,
456 456 )
457 457 coreconfigitem(
458 458 b'email', b'charsets', default=list,
459 459 )
460 460 coreconfigitem(
461 461 b'email', b'from', default=None,
462 462 )
463 463 coreconfigitem(
464 464 b'email', b'method', default=b'smtp',
465 465 )
466 466 coreconfigitem(
467 467 b'email', b'reply-to', default=None,
468 468 )
469 469 coreconfigitem(
470 470 b'email', b'to', default=None,
471 471 )
472 472 coreconfigitem(
473 473 b'experimental', b'archivemetatemplate', default=dynamicdefault,
474 474 )
475 475 coreconfigitem(
476 476 b'experimental', b'auto-publish', default=b'publish',
477 477 )
478 478 coreconfigitem(
479 479 b'experimental', b'bundle-phases', default=False,
480 480 )
481 481 coreconfigitem(
482 482 b'experimental', b'bundle2-advertise', default=True,
483 483 )
484 484 coreconfigitem(
485 485 b'experimental', b'bundle2-output-capture', default=False,
486 486 )
487 487 coreconfigitem(
488 488 b'experimental', b'bundle2.pushback', default=False,
489 489 )
490 490 coreconfigitem(
491 491 b'experimental', b'bundle2lazylocking', default=False,
492 492 )
493 493 coreconfigitem(
494 494 b'experimental', b'bundlecomplevel', default=None,
495 495 )
496 496 coreconfigitem(
497 497 b'experimental', b'bundlecomplevel.bzip2', default=None,
498 498 )
499 499 coreconfigitem(
500 500 b'experimental', b'bundlecomplevel.gzip', default=None,
501 501 )
502 502 coreconfigitem(
503 503 b'experimental', b'bundlecomplevel.none', default=None,
504 504 )
505 505 coreconfigitem(
506 506 b'experimental', b'bundlecomplevel.zstd', default=None,
507 507 )
508 508 coreconfigitem(
509 509 b'experimental', b'changegroup3', default=False,
510 510 )
511 511 coreconfigitem(
512 512 b'experimental', b'cleanup-as-archived', default=False,
513 513 )
514 514 coreconfigitem(
515 515 b'experimental', b'clientcompressionengines', default=list,
516 516 )
517 517 coreconfigitem(
518 518 b'experimental', b'copytrace', default=b'on',
519 519 )
520 520 coreconfigitem(
521 521 b'experimental', b'copytrace.movecandidateslimit', default=100,
522 522 )
523 523 coreconfigitem(
524 524 b'experimental', b'copytrace.sourcecommitlimit', default=100,
525 525 )
526 526 coreconfigitem(
527 527 b'experimental', b'copies.read-from', default=b"filelog-only",
528 528 )
529 529 coreconfigitem(
530 530 b'experimental', b'copies.write-to', default=b'filelog-only',
531 531 )
532 532 coreconfigitem(
533 533 b'experimental', b'crecordtest', default=None,
534 534 )
535 535 coreconfigitem(
536 536 b'experimental', b'directaccess', default=False,
537 537 )
538 538 coreconfigitem(
539 539 b'experimental', b'directaccess.revnums', default=False,
540 540 )
541 541 coreconfigitem(
542 542 b'experimental', b'editortmpinhg', default=False,
543 543 )
544 544 coreconfigitem(
545 545 b'experimental', b'evolution', default=list,
546 546 )
547 547 coreconfigitem(
548 548 b'experimental',
549 549 b'evolution.allowdivergence',
550 550 default=False,
551 551 alias=[(b'experimental', b'allowdivergence')],
552 552 )
553 553 coreconfigitem(
554 554 b'experimental', b'evolution.allowunstable', default=None,
555 555 )
556 556 coreconfigitem(
557 557 b'experimental', b'evolution.createmarkers', default=None,
558 558 )
559 559 coreconfigitem(
560 560 b'experimental',
561 561 b'evolution.effect-flags',
562 562 default=True,
563 563 alias=[(b'experimental', b'effect-flags')],
564 564 )
565 565 coreconfigitem(
566 566 b'experimental', b'evolution.exchange', default=None,
567 567 )
568 568 coreconfigitem(
569 569 b'experimental', b'evolution.bundle-obsmarker', default=False,
570 570 )
571 571 coreconfigitem(
572 572 b'experimental', b'log.topo', default=False,
573 573 )
574 574 coreconfigitem(
575 575 b'experimental', b'evolution.report-instabilities', default=True,
576 576 )
577 577 coreconfigitem(
578 578 b'experimental', b'evolution.track-operation', default=True,
579 579 )
580 580 # repo-level config to exclude a revset visibility
581 581 #
582 582 # The target use case is to use `share` to expose different subset of the same
583 583 # repository, especially server side. See also `server.view`.
584 584 coreconfigitem(
585 585 b'experimental', b'extra-filter-revs', default=None,
586 586 )
587 587 coreconfigitem(
588 588 b'experimental', b'maxdeltachainspan', default=-1,
589 589 )
590 590 coreconfigitem(
591 591 b'experimental', b'mergetempdirprefix', default=None,
592 592 )
593 593 coreconfigitem(
594 594 b'experimental', b'mmapindexthreshold', default=None,
595 595 )
596 596 coreconfigitem(
597 597 b'experimental', b'narrow', default=False,
598 598 )
599 599 coreconfigitem(
600 600 b'experimental', b'nonnormalparanoidcheck', default=False,
601 601 )
602 602 coreconfigitem(
603 603 b'experimental', b'exportableenviron', default=list,
604 604 )
605 605 coreconfigitem(
606 606 b'experimental', b'extendedheader.index', default=None,
607 607 )
608 608 coreconfigitem(
609 609 b'experimental', b'extendedheader.similarity', default=False,
610 610 )
611 611 coreconfigitem(
612 612 b'experimental', b'graphshorten', default=False,
613 613 )
614 614 coreconfigitem(
615 615 b'experimental', b'graphstyle.parent', default=dynamicdefault,
616 616 )
617 617 coreconfigitem(
618 618 b'experimental', b'graphstyle.missing', default=dynamicdefault,
619 619 )
620 620 coreconfigitem(
621 621 b'experimental', b'graphstyle.grandparent', default=dynamicdefault,
622 622 )
623 623 coreconfigitem(
624 624 b'experimental', b'hook-track-tags', default=False,
625 625 )
626 626 coreconfigitem(
627 627 b'experimental', b'httppeer.advertise-v2', default=False,
628 628 )
629 629 coreconfigitem(
630 630 b'experimental', b'httppeer.v2-encoder-order', default=None,
631 631 )
632 632 coreconfigitem(
633 633 b'experimental', b'httppostargs', default=False,
634 634 )
635 635 coreconfigitem(
636 636 b'experimental', b'mergedriver', default=None,
637 637 )
638 638 coreconfigitem(b'experimental', b'nointerrupt', default=False)
639 639 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
640 640
641 641 coreconfigitem(
642 642 b'experimental', b'obsmarkers-exchange-debug', default=False,
643 643 )
644 644 coreconfigitem(
645 645 b'experimental', b'remotenames', default=False,
646 646 )
647 647 coreconfigitem(
648 648 b'experimental', b'removeemptydirs', default=True,
649 649 )
650 650 coreconfigitem(
651 651 b'experimental', b'revert.interactive.select-to-keep', default=False,
652 652 )
653 653 coreconfigitem(
654 654 b'experimental', b'revisions.prefixhexnode', default=False,
655 655 )
656 656 coreconfigitem(
657 657 b'experimental', b'revlogv2', default=None,
658 658 )
659 659 coreconfigitem(
660 660 b'experimental', b'revisions.disambiguatewithin', default=None,
661 661 )
662 662 coreconfigitem(
663 663 b'experimental', b'rust.index', default=False,
664 664 )
665 665 coreconfigitem(
666 666 b'experimental', b'server.filesdata.recommended-batch-size', default=50000,
667 667 )
668 668 coreconfigitem(
669 669 b'experimental',
670 670 b'server.manifestdata.recommended-batch-size',
671 671 default=100000,
672 672 )
673 673 coreconfigitem(
674 674 b'experimental', b'server.stream-narrow-clones', default=False,
675 675 )
676 676 coreconfigitem(
677 677 b'experimental', b'single-head-per-branch', default=False,
678 678 )
679 679 coreconfigitem(
680 680 b'experimental',
681 681 b'single-head-per-branch:account-closed-heads',
682 682 default=False,
683 683 )
684 684 coreconfigitem(
685 685 b'experimental', b'sshserver.support-v2', default=False,
686 686 )
687 687 coreconfigitem(
688 688 b'experimental', b'sparse-read', default=False,
689 689 )
690 690 coreconfigitem(
691 691 b'experimental', b'sparse-read.density-threshold', default=0.50,
692 692 )
693 693 coreconfigitem(
694 694 b'experimental', b'sparse-read.min-gap-size', default=b'65K',
695 695 )
696 696 coreconfigitem(
697 697 b'experimental', b'treemanifest', default=False,
698 698 )
699 699 coreconfigitem(
700 700 b'experimental', b'update.atomic-file', default=False,
701 701 )
702 702 coreconfigitem(
703 703 b'experimental', b'sshpeer.advertise-v2', default=False,
704 704 )
705 705 coreconfigitem(
706 706 b'experimental', b'web.apiserver', default=False,
707 707 )
708 708 coreconfigitem(
709 709 b'experimental', b'web.api.http-v2', default=False,
710 710 )
711 711 coreconfigitem(
712 712 b'experimental', b'web.api.debugreflect', default=False,
713 713 )
714 714 coreconfigitem(
715 715 b'experimental', b'worker.wdir-get-thread-safe', default=False,
716 716 )
717 717 coreconfigitem(
718 718 b'experimental', b'worker.repository-upgrade', default=False,
719 719 )
720 720 coreconfigitem(
721 721 b'experimental', b'xdiff', default=False,
722 722 )
723 723 coreconfigitem(
724 724 b'extensions', b'.*', default=None, generic=True,
725 725 )
726 726 coreconfigitem(
727 727 b'extdata', b'.*', default=None, generic=True,
728 728 )
729 729 coreconfigitem(
730 730 b'format', b'bookmarks-in-store', default=False,
731 731 )
732 732 coreconfigitem(
733 733 b'format', b'chunkcachesize', default=None, experimental=True,
734 734 )
735 735 coreconfigitem(
736 736 b'format', b'dotencode', default=True,
737 737 )
738 738 coreconfigitem(
739 739 b'format', b'generaldelta', default=False, experimental=True,
740 740 )
741 741 coreconfigitem(
742 742 b'format', b'manifestcachesize', default=None, experimental=True,
743 743 )
744 744 coreconfigitem(
745 745 b'format', b'maxchainlen', default=dynamicdefault, experimental=True,
746 746 )
747 747 coreconfigitem(
748 748 b'format', b'obsstore-version', default=None,
749 749 )
750 750 coreconfigitem(
751 751 b'format', b'sparse-revlog', default=True,
752 752 )
753 753 coreconfigitem(
754 754 b'format',
755 755 b'revlog-compression',
756 756 default=lambda: [b'zlib'],
757 757 alias=[(b'experimental', b'format.compression')],
758 758 )
759 759 coreconfigitem(
760 760 b'format', b'usefncache', default=True,
761 761 )
762 762 coreconfigitem(
763 763 b'format', b'usegeneraldelta', default=True,
764 764 )
765 765 coreconfigitem(
766 766 b'format', b'usestore', default=True,
767 767 )
768 768 # Right now, the only efficient implement of the nodemap logic is in Rust, so
769 769 # the persistent nodemap feature needs to stay experimental as long as the Rust
770 770 # extensions are an experimental feature.
771 771 coreconfigitem(
772 772 b'format', b'use-persistent-nodemap', default=False, experimental=True
773 773 )
774 774 coreconfigitem(
775 775 b'format',
776 776 b'exp-use-copies-side-data-changeset',
777 777 default=False,
778 778 experimental=True,
779 779 )
780 780 coreconfigitem(
781 781 b'format', b'exp-use-side-data', default=False, experimental=True,
782 782 )
783 783 coreconfigitem(
784 784 b'format', b'internal-phase', default=False, experimental=True,
785 785 )
786 786 coreconfigitem(
787 787 b'fsmonitor', b'warn_when_unused', default=True,
788 788 )
789 789 coreconfigitem(
790 790 b'fsmonitor', b'warn_update_file_count', default=50000,
791 791 )
792 792 coreconfigitem(
793 793 b'help', br'hidden-command\..*', default=False, generic=True,
794 794 )
795 795 coreconfigitem(
796 796 b'help', br'hidden-topic\..*', default=False, generic=True,
797 797 )
798 798 coreconfigitem(
799 799 b'hooks', b'.*', default=dynamicdefault, generic=True,
800 800 )
801 801 coreconfigitem(
802 802 b'hgweb-paths', b'.*', default=list, generic=True,
803 803 )
804 804 coreconfigitem(
805 805 b'hostfingerprints', b'.*', default=list, generic=True,
806 806 )
807 807 coreconfigitem(
808 808 b'hostsecurity', b'ciphers', default=None,
809 809 )
810 810 coreconfigitem(
811 811 b'hostsecurity', b'minimumprotocol', default=dynamicdefault,
812 812 )
813 813 coreconfigitem(
814 814 b'hostsecurity',
815 815 b'.*:minimumprotocol$',
816 816 default=dynamicdefault,
817 817 generic=True,
818 818 )
819 819 coreconfigitem(
820 820 b'hostsecurity', b'.*:ciphers$', default=dynamicdefault, generic=True,
821 821 )
822 822 coreconfigitem(
823 823 b'hostsecurity', b'.*:fingerprints$', default=list, generic=True,
824 824 )
825 825 coreconfigitem(
826 826 b'hostsecurity', b'.*:verifycertsfile$', default=None, generic=True,
827 827 )
828 828
829 829 coreconfigitem(
830 830 b'http_proxy', b'always', default=False,
831 831 )
832 832 coreconfigitem(
833 833 b'http_proxy', b'host', default=None,
834 834 )
835 835 coreconfigitem(
836 836 b'http_proxy', b'no', default=list,
837 837 )
838 838 coreconfigitem(
839 839 b'http_proxy', b'passwd', default=None,
840 840 )
841 841 coreconfigitem(
842 842 b'http_proxy', b'user', default=None,
843 843 )
844 844
845 845 coreconfigitem(
846 846 b'http', b'timeout', default=None,
847 847 )
848 848
849 849 coreconfigitem(
850 850 b'logtoprocess', b'commandexception', default=None,
851 851 )
852 852 coreconfigitem(
853 853 b'logtoprocess', b'commandfinish', default=None,
854 854 )
855 855 coreconfigitem(
856 856 b'logtoprocess', b'command', default=None,
857 857 )
858 858 coreconfigitem(
859 859 b'logtoprocess', b'develwarn', default=None,
860 860 )
861 861 coreconfigitem(
862 862 b'logtoprocess', b'uiblocked', default=None,
863 863 )
864 864 coreconfigitem(
865 865 b'merge', b'checkunknown', default=b'abort',
866 866 )
867 867 coreconfigitem(
868 868 b'merge', b'checkignored', default=b'abort',
869 869 )
870 870 coreconfigitem(
871 871 b'experimental', b'merge.checkpathconflicts', default=False,
872 872 )
873 873 coreconfigitem(
874 874 b'merge', b'followcopies', default=True,
875 875 )
876 876 coreconfigitem(
877 877 b'merge', b'on-failure', default=b'continue',
878 878 )
879 879 coreconfigitem(
880 880 b'merge', b'preferancestor', default=lambda: [b'*'], experimental=True,
881 881 )
882 882 coreconfigitem(
883 883 b'merge', b'strict-capability-check', default=False,
884 884 )
885 885 coreconfigitem(
886 886 b'merge-tools', b'.*', default=None, generic=True,
887 887 )
888 888 coreconfigitem(
889 889 b'merge-tools',
890 890 br'.*\.args$',
891 891 default=b"$local $base $other",
892 892 generic=True,
893 893 priority=-1,
894 894 )
895 895 coreconfigitem(
896 896 b'merge-tools', br'.*\.binary$', default=False, generic=True, priority=-1,
897 897 )
898 898 coreconfigitem(
899 899 b'merge-tools', br'.*\.check$', default=list, generic=True, priority=-1,
900 900 )
901 901 coreconfigitem(
902 902 b'merge-tools',
903 903 br'.*\.checkchanged$',
904 904 default=False,
905 905 generic=True,
906 906 priority=-1,
907 907 )
908 908 coreconfigitem(
909 909 b'merge-tools',
910 910 br'.*\.executable$',
911 911 default=dynamicdefault,
912 912 generic=True,
913 913 priority=-1,
914 914 )
915 915 coreconfigitem(
916 916 b'merge-tools', br'.*\.fixeol$', default=False, generic=True, priority=-1,
917 917 )
918 918 coreconfigitem(
919 919 b'merge-tools', br'.*\.gui$', default=False, generic=True, priority=-1,
920 920 )
921 921 coreconfigitem(
922 922 b'merge-tools',
923 923 br'.*\.mergemarkers$',
924 924 default=b'basic',
925 925 generic=True,
926 926 priority=-1,
927 927 )
928 928 coreconfigitem(
929 929 b'merge-tools',
930 930 br'.*\.mergemarkertemplate$',
931 931 default=dynamicdefault, # take from ui.mergemarkertemplate
932 932 generic=True,
933 933 priority=-1,
934 934 )
935 935 coreconfigitem(
936 936 b'merge-tools', br'.*\.priority$', default=0, generic=True, priority=-1,
937 937 )
938 938 coreconfigitem(
939 939 b'merge-tools',
940 940 br'.*\.premerge$',
941 941 default=dynamicdefault,
942 942 generic=True,
943 943 priority=-1,
944 944 )
945 945 coreconfigitem(
946 946 b'merge-tools', br'.*\.symlink$', default=False, generic=True, priority=-1,
947 947 )
948 948 coreconfigitem(
949 949 b'pager', b'attend-.*', default=dynamicdefault, generic=True,
950 950 )
951 951 coreconfigitem(
952 952 b'pager', b'ignore', default=list,
953 953 )
954 954 coreconfigitem(
955 955 b'pager', b'pager', default=dynamicdefault,
956 956 )
957 957 coreconfigitem(
958 958 b'patch', b'eol', default=b'strict',
959 959 )
960 960 coreconfigitem(
961 961 b'patch', b'fuzz', default=2,
962 962 )
963 963 coreconfigitem(
964 964 b'paths', b'default', default=None,
965 965 )
966 966 coreconfigitem(
967 967 b'paths', b'default-push', default=None,
968 968 )
969 969 coreconfigitem(
970 970 b'paths', b'.*', default=None, generic=True,
971 971 )
972 972 coreconfigitem(
973 973 b'phases', b'checksubrepos', default=b'follow',
974 974 )
975 975 coreconfigitem(
976 976 b'phases', b'new-commit', default=b'draft',
977 977 )
978 978 coreconfigitem(
979 979 b'phases', b'publish', default=True,
980 980 )
981 981 coreconfigitem(
982 982 b'profiling', b'enabled', default=False,
983 983 )
984 984 coreconfigitem(
985 985 b'profiling', b'format', default=b'text',
986 986 )
987 987 coreconfigitem(
988 988 b'profiling', b'freq', default=1000,
989 989 )
990 990 coreconfigitem(
991 991 b'profiling', b'limit', default=30,
992 992 )
993 993 coreconfigitem(
994 994 b'profiling', b'nested', default=0,
995 995 )
996 996 coreconfigitem(
997 997 b'profiling', b'output', default=None,
998 998 )
999 999 coreconfigitem(
1000 1000 b'profiling', b'showmax', default=0.999,
1001 1001 )
1002 1002 coreconfigitem(
1003 1003 b'profiling', b'showmin', default=dynamicdefault,
1004 1004 )
1005 1005 coreconfigitem(
1006 1006 b'profiling', b'showtime', default=True,
1007 1007 )
1008 1008 coreconfigitem(
1009 1009 b'profiling', b'sort', default=b'inlinetime',
1010 1010 )
1011 1011 coreconfigitem(
1012 1012 b'profiling', b'statformat', default=b'hotpath',
1013 1013 )
1014 1014 coreconfigitem(
1015 1015 b'profiling', b'time-track', default=dynamicdefault,
1016 1016 )
1017 1017 coreconfigitem(
1018 1018 b'profiling', b'type', default=b'stat',
1019 1019 )
1020 1020 coreconfigitem(
1021 1021 b'progress', b'assume-tty', default=False,
1022 1022 )
1023 1023 coreconfigitem(
1024 1024 b'progress', b'changedelay', default=1,
1025 1025 )
1026 1026 coreconfigitem(
1027 1027 b'progress', b'clear-complete', default=True,
1028 1028 )
1029 1029 coreconfigitem(
1030 1030 b'progress', b'debug', default=False,
1031 1031 )
1032 1032 coreconfigitem(
1033 1033 b'progress', b'delay', default=3,
1034 1034 )
1035 1035 coreconfigitem(
1036 1036 b'progress', b'disable', default=False,
1037 1037 )
1038 1038 coreconfigitem(
1039 1039 b'progress', b'estimateinterval', default=60.0,
1040 1040 )
1041 1041 coreconfigitem(
1042 1042 b'progress',
1043 1043 b'format',
1044 1044 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1045 1045 )
1046 1046 coreconfigitem(
1047 1047 b'progress', b'refresh', default=0.1,
1048 1048 )
1049 1049 coreconfigitem(
1050 1050 b'progress', b'width', default=dynamicdefault,
1051 1051 )
1052 1052 coreconfigitem(
1053 1053 b'pull', b'confirm', default=False,
1054 1054 )
1055 1055 coreconfigitem(
1056 1056 b'push', b'pushvars.server', default=False,
1057 1057 )
1058 1058 coreconfigitem(
1059 1059 b'rewrite',
1060 1060 b'backup-bundle',
1061 1061 default=True,
1062 1062 alias=[(b'ui', b'history-editing-backup')],
1063 1063 )
1064 1064 coreconfigitem(
1065 1065 b'rewrite', b'update-timestamp', default=False,
1066 1066 )
1067 1067 coreconfigitem(
1068 1068 b'storage', b'new-repo-backend', default=b'revlogv1', experimental=True,
1069 1069 )
1070 1070 coreconfigitem(
1071 1071 b'storage',
1072 1072 b'revlog.optimize-delta-parent-choice',
1073 1073 default=True,
1074 1074 alias=[(b'format', b'aggressivemergedeltas')],
1075 1075 )
1076 1076 # experimental as long as rust is experimental (or a C version is implemented)
1077 1077 coreconfigitem(
1078 1078 b'storage', b'revlog.nodemap.mmap', default=True, experimental=True
1079 1079 )
1080 1080 # experimental as long as format.use-persistent-nodemap is.
1081 1081 coreconfigitem(
1082 1082 b'storage', b'revlog.nodemap.mode', default=b'compat', experimental=True
1083 1083 )
1084 1084 coreconfigitem(
1085 1085 b'storage', b'revlog.reuse-external-delta', default=True,
1086 1086 )
1087 1087 coreconfigitem(
1088 1088 b'storage', b'revlog.reuse-external-delta-parent', default=None,
1089 1089 )
1090 1090 coreconfigitem(
1091 1091 b'storage', b'revlog.zlib.level', default=None,
1092 1092 )
1093 1093 coreconfigitem(
1094 1094 b'storage', b'revlog.zstd.level', default=None,
1095 1095 )
1096 1096 coreconfigitem(
1097 1097 b'server', b'bookmarks-pushkey-compat', default=True,
1098 1098 )
1099 1099 coreconfigitem(
1100 1100 b'server', b'bundle1', default=True,
1101 1101 )
1102 1102 coreconfigitem(
1103 1103 b'server', b'bundle1gd', default=None,
1104 1104 )
1105 1105 coreconfigitem(
1106 1106 b'server', b'bundle1.pull', default=None,
1107 1107 )
1108 1108 coreconfigitem(
1109 1109 b'server', b'bundle1gd.pull', default=None,
1110 1110 )
1111 1111 coreconfigitem(
1112 1112 b'server', b'bundle1.push', default=None,
1113 1113 )
1114 1114 coreconfigitem(
1115 1115 b'server', b'bundle1gd.push', default=None,
1116 1116 )
1117 1117 coreconfigitem(
1118 1118 b'server',
1119 1119 b'bundle2.stream',
1120 1120 default=True,
1121 1121 alias=[(b'experimental', b'bundle2.stream')],
1122 1122 )
1123 1123 coreconfigitem(
1124 1124 b'server', b'compressionengines', default=list,
1125 1125 )
1126 1126 coreconfigitem(
1127 1127 b'server', b'concurrent-push-mode', default=b'check-related',
1128 1128 )
1129 1129 coreconfigitem(
1130 1130 b'server', b'disablefullbundle', default=False,
1131 1131 )
1132 1132 coreconfigitem(
1133 1133 b'server', b'maxhttpheaderlen', default=1024,
1134 1134 )
1135 1135 coreconfigitem(
1136 1136 b'server', b'pullbundle', default=False,
1137 1137 )
1138 1138 coreconfigitem(
1139 1139 b'server', b'preferuncompressed', default=False,
1140 1140 )
1141 1141 coreconfigitem(
1142 1142 b'server', b'streamunbundle', default=False,
1143 1143 )
1144 1144 coreconfigitem(
1145 1145 b'server', b'uncompressed', default=True,
1146 1146 )
1147 1147 coreconfigitem(
1148 1148 b'server', b'uncompressedallowsecret', default=False,
1149 1149 )
1150 1150 coreconfigitem(
1151 1151 b'server', b'view', default=b'served',
1152 1152 )
1153 1153 coreconfigitem(
1154 1154 b'server', b'validate', default=False,
1155 1155 )
1156 1156 coreconfigitem(
1157 1157 b'server', b'zliblevel', default=-1,
1158 1158 )
1159 1159 coreconfigitem(
1160 1160 b'server', b'zstdlevel', default=3,
1161 1161 )
1162 1162 coreconfigitem(
1163 1163 b'share', b'pool', default=None,
1164 1164 )
1165 1165 coreconfigitem(
1166 1166 b'share', b'poolnaming', default=b'identity',
1167 1167 )
1168 1168 coreconfigitem(
1169 1169 b'shelve', b'maxbackups', default=10,
1170 1170 )
1171 1171 coreconfigitem(
1172 1172 b'smtp', b'host', default=None,
1173 1173 )
1174 1174 coreconfigitem(
1175 1175 b'smtp', b'local_hostname', default=None,
1176 1176 )
1177 1177 coreconfigitem(
1178 1178 b'smtp', b'password', default=None,
1179 1179 )
1180 1180 coreconfigitem(
1181 1181 b'smtp', b'port', default=dynamicdefault,
1182 1182 )
1183 1183 coreconfigitem(
1184 1184 b'smtp', b'tls', default=b'none',
1185 1185 )
1186 1186 coreconfigitem(
1187 1187 b'smtp', b'username', default=None,
1188 1188 )
1189 1189 coreconfigitem(
1190 1190 b'sparse', b'missingwarning', default=True, experimental=True,
1191 1191 )
1192 1192 coreconfigitem(
1193 1193 b'subrepos',
1194 1194 b'allowed',
1195 1195 default=dynamicdefault, # to make backporting simpler
1196 1196 )
1197 1197 coreconfigitem(
1198 1198 b'subrepos', b'hg:allowed', default=dynamicdefault,
1199 1199 )
1200 1200 coreconfigitem(
1201 1201 b'subrepos', b'git:allowed', default=dynamicdefault,
1202 1202 )
1203 1203 coreconfigitem(
1204 1204 b'subrepos', b'svn:allowed', default=dynamicdefault,
1205 1205 )
1206 1206 coreconfigitem(
1207 1207 b'templates', b'.*', default=None, generic=True,
1208 1208 )
1209 1209 coreconfigitem(
1210 1210 b'templateconfig', b'.*', default=dynamicdefault, generic=True,
1211 1211 )
1212 1212 coreconfigitem(
1213 1213 b'trusted', b'groups', default=list,
1214 1214 )
1215 1215 coreconfigitem(
1216 1216 b'trusted', b'users', default=list,
1217 1217 )
1218 1218 coreconfigitem(
1219 1219 b'ui', b'_usedassubrepo', default=False,
1220 1220 )
1221 1221 coreconfigitem(
1222 1222 b'ui', b'allowemptycommit', default=False,
1223 1223 )
1224 1224 coreconfigitem(
1225 1225 b'ui', b'archivemeta', default=True,
1226 1226 )
1227 1227 coreconfigitem(
1228 1228 b'ui', b'askusername', default=False,
1229 1229 )
1230 1230 coreconfigitem(
1231 1231 b'ui', b'clonebundlefallback', default=False,
1232 1232 )
1233 1233 coreconfigitem(
1234 1234 b'ui', b'clonebundleprefers', default=list,
1235 1235 )
1236 1236 coreconfigitem(
1237 1237 b'ui', b'clonebundles', default=True,
1238 1238 )
1239 1239 coreconfigitem(
1240 1240 b'ui', b'color', default=b'auto',
1241 1241 )
1242 1242 coreconfigitem(
1243 1243 b'ui', b'commitsubrepos', default=False,
1244 1244 )
1245 1245 coreconfigitem(
1246 1246 b'ui', b'debug', default=False,
1247 1247 )
1248 1248 coreconfigitem(
1249 1249 b'ui', b'debugger', default=None,
1250 1250 )
1251 1251 coreconfigitem(
1252 1252 b'ui', b'editor', default=dynamicdefault,
1253 1253 )
1254 1254 coreconfigitem(
1255 1255 b'ui', b'fallbackencoding', default=None,
1256 1256 )
1257 1257 coreconfigitem(
1258 1258 b'ui', b'forcecwd', default=None,
1259 1259 )
1260 1260 coreconfigitem(
1261 1261 b'ui', b'forcemerge', default=None,
1262 1262 )
1263 1263 coreconfigitem(
1264 1264 b'ui', b'formatdebug', default=False,
1265 1265 )
1266 1266 coreconfigitem(
1267 1267 b'ui', b'formatjson', default=False,
1268 1268 )
1269 1269 coreconfigitem(
1270 1270 b'ui', b'formatted', default=None,
1271 1271 )
1272 1272 coreconfigitem(
1273 1273 b'ui', b'graphnodetemplate', default=None,
1274 1274 )
1275 1275 coreconfigitem(
1276 1276 b'ui', b'interactive', default=None,
1277 1277 )
1278 1278 coreconfigitem(
1279 1279 b'ui', b'interface', default=None,
1280 1280 )
1281 1281 coreconfigitem(
1282 1282 b'ui', b'interface.chunkselector', default=None,
1283 1283 )
1284 1284 coreconfigitem(
1285 1285 b'ui', b'large-file-limit', default=10000000,
1286 1286 )
1287 1287 coreconfigitem(
1288 1288 b'ui', b'logblockedtimes', default=False,
1289 1289 )
1290 1290 coreconfigitem(
1291 1291 b'ui', b'logtemplate', default=None,
1292 1292 )
1293 1293 coreconfigitem(
1294 1294 b'ui', b'merge', default=None,
1295 1295 )
1296 1296 coreconfigitem(
1297 1297 b'ui', b'mergemarkers', default=b'basic',
1298 1298 )
1299 1299 coreconfigitem(
1300 1300 b'ui',
1301 1301 b'mergemarkertemplate',
1302 1302 default=(
1303 1303 b'{node|short} '
1304 1304 b'{ifeq(tags, "tip", "", '
1305 1305 b'ifeq(tags, "", "", "{tags} "))}'
1306 1306 b'{if(bookmarks, "{bookmarks} ")}'
1307 1307 b'{ifeq(branch, "default", "", "{branch} ")}'
1308 1308 b'- {author|user}: {desc|firstline}'
1309 1309 ),
1310 1310 )
1311 1311 coreconfigitem(
1312 1312 b'ui', b'message-output', default=b'stdio',
1313 1313 )
1314 1314 coreconfigitem(
1315 1315 b'ui', b'nontty', default=False,
1316 1316 )
1317 1317 coreconfigitem(
1318 1318 b'ui', b'origbackuppath', default=None,
1319 1319 )
1320 1320 coreconfigitem(
1321 1321 b'ui', b'paginate', default=True,
1322 1322 )
1323 1323 coreconfigitem(
1324 1324 b'ui', b'patch', default=None,
1325 1325 )
1326 1326 coreconfigitem(
1327 1327 b'ui', b'pre-merge-tool-output-template', default=None,
1328 1328 )
1329 1329 coreconfigitem(
1330 1330 b'ui', b'portablefilenames', default=b'warn',
1331 1331 )
1332 1332 coreconfigitem(
1333 1333 b'ui', b'promptecho', default=False,
1334 1334 )
1335 1335 coreconfigitem(
1336 1336 b'ui', b'quiet', default=False,
1337 1337 )
1338 1338 coreconfigitem(
1339 1339 b'ui', b'quietbookmarkmove', default=False,
1340 1340 )
1341 1341 coreconfigitem(
1342 1342 b'ui', b'relative-paths', default=b'legacy',
1343 1343 )
1344 1344 coreconfigitem(
1345 1345 b'ui', b'remotecmd', default=b'hg',
1346 1346 )
1347 1347 coreconfigitem(
1348 1348 b'ui', b'report_untrusted', default=True,
1349 1349 )
1350 1350 coreconfigitem(
1351 1351 b'ui', b'rollback', default=True,
1352 1352 )
1353 1353 coreconfigitem(
1354 1354 b'ui', b'signal-safe-lock', default=True,
1355 1355 )
1356 1356 coreconfigitem(
1357 1357 b'ui', b'slash', default=False,
1358 1358 )
1359 1359 coreconfigitem(
1360 1360 b'ui', b'ssh', default=b'ssh',
1361 1361 )
1362 1362 coreconfigitem(
1363 1363 b'ui', b'ssherrorhint', default=None,
1364 1364 )
1365 1365 coreconfigitem(
1366 1366 b'ui', b'statuscopies', default=False,
1367 1367 )
1368 1368 coreconfigitem(
1369 1369 b'ui', b'strict', default=False,
1370 1370 )
1371 1371 coreconfigitem(
1372 1372 b'ui', b'style', default=b'',
1373 1373 )
1374 1374 coreconfigitem(
1375 1375 b'ui', b'supportcontact', default=None,
1376 1376 )
1377 1377 coreconfigitem(
1378 1378 b'ui', b'textwidth', default=78,
1379 1379 )
1380 1380 coreconfigitem(
1381 1381 b'ui', b'timeout', default=b'600',
1382 1382 )
1383 1383 coreconfigitem(
1384 1384 b'ui', b'timeout.warn', default=0,
1385 1385 )
1386 1386 coreconfigitem(
1387 b'ui', b'timestamp-output', default=False,
1388 )
1389 coreconfigitem(
1387 1390 b'ui', b'traceback', default=False,
1388 1391 )
1389 1392 coreconfigitem(
1390 1393 b'ui', b'tweakdefaults', default=False,
1391 1394 )
1392 1395 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
1393 1396 coreconfigitem(
1394 1397 b'ui', b'verbose', default=False,
1395 1398 )
1396 1399 coreconfigitem(
1397 1400 b'verify', b'skipflags', default=None,
1398 1401 )
1399 1402 coreconfigitem(
1400 1403 b'web', b'allowbz2', default=False,
1401 1404 )
1402 1405 coreconfigitem(
1403 1406 b'web', b'allowgz', default=False,
1404 1407 )
1405 1408 coreconfigitem(
1406 1409 b'web', b'allow-pull', alias=[(b'web', b'allowpull')], default=True,
1407 1410 )
1408 1411 coreconfigitem(
1409 1412 b'web', b'allow-push', alias=[(b'web', b'allow_push')], default=list,
1410 1413 )
1411 1414 coreconfigitem(
1412 1415 b'web', b'allowzip', default=False,
1413 1416 )
1414 1417 coreconfigitem(
1415 1418 b'web', b'archivesubrepos', default=False,
1416 1419 )
1417 1420 coreconfigitem(
1418 1421 b'web', b'cache', default=True,
1419 1422 )
1420 1423 coreconfigitem(
1421 1424 b'web', b'comparisoncontext', default=5,
1422 1425 )
1423 1426 coreconfigitem(
1424 1427 b'web', b'contact', default=None,
1425 1428 )
1426 1429 coreconfigitem(
1427 1430 b'web', b'deny_push', default=list,
1428 1431 )
1429 1432 coreconfigitem(
1430 1433 b'web', b'guessmime', default=False,
1431 1434 )
1432 1435 coreconfigitem(
1433 1436 b'web', b'hidden', default=False,
1434 1437 )
1435 1438 coreconfigitem(
1436 1439 b'web', b'labels', default=list,
1437 1440 )
1438 1441 coreconfigitem(
1439 1442 b'web', b'logoimg', default=b'hglogo.png',
1440 1443 )
1441 1444 coreconfigitem(
1442 1445 b'web', b'logourl', default=b'https://mercurial-scm.org/',
1443 1446 )
1444 1447 coreconfigitem(
1445 1448 b'web', b'accesslog', default=b'-',
1446 1449 )
1447 1450 coreconfigitem(
1448 1451 b'web', b'address', default=b'',
1449 1452 )
1450 1453 coreconfigitem(
1451 1454 b'web', b'allow-archive', alias=[(b'web', b'allow_archive')], default=list,
1452 1455 )
1453 1456 coreconfigitem(
1454 1457 b'web', b'allow_read', default=list,
1455 1458 )
1456 1459 coreconfigitem(
1457 1460 b'web', b'baseurl', default=None,
1458 1461 )
1459 1462 coreconfigitem(
1460 1463 b'web', b'cacerts', default=None,
1461 1464 )
1462 1465 coreconfigitem(
1463 1466 b'web', b'certificate', default=None,
1464 1467 )
1465 1468 coreconfigitem(
1466 1469 b'web', b'collapse', default=False,
1467 1470 )
1468 1471 coreconfigitem(
1469 1472 b'web', b'csp', default=None,
1470 1473 )
1471 1474 coreconfigitem(
1472 1475 b'web', b'deny_read', default=list,
1473 1476 )
1474 1477 coreconfigitem(
1475 1478 b'web', b'descend', default=True,
1476 1479 )
1477 1480 coreconfigitem(
1478 1481 b'web', b'description', default=b"",
1479 1482 )
1480 1483 coreconfigitem(
1481 1484 b'web', b'encoding', default=lambda: encoding.encoding,
1482 1485 )
1483 1486 coreconfigitem(
1484 1487 b'web', b'errorlog', default=b'-',
1485 1488 )
1486 1489 coreconfigitem(
1487 1490 b'web', b'ipv6', default=False,
1488 1491 )
1489 1492 coreconfigitem(
1490 1493 b'web', b'maxchanges', default=10,
1491 1494 )
1492 1495 coreconfigitem(
1493 1496 b'web', b'maxfiles', default=10,
1494 1497 )
1495 1498 coreconfigitem(
1496 1499 b'web', b'maxshortchanges', default=60,
1497 1500 )
1498 1501 coreconfigitem(
1499 1502 b'web', b'motd', default=b'',
1500 1503 )
1501 1504 coreconfigitem(
1502 1505 b'web', b'name', default=dynamicdefault,
1503 1506 )
1504 1507 coreconfigitem(
1505 1508 b'web', b'port', default=8000,
1506 1509 )
1507 1510 coreconfigitem(
1508 1511 b'web', b'prefix', default=b'',
1509 1512 )
1510 1513 coreconfigitem(
1511 1514 b'web', b'push_ssl', default=True,
1512 1515 )
1513 1516 coreconfigitem(
1514 1517 b'web', b'refreshinterval', default=20,
1515 1518 )
1516 1519 coreconfigitem(
1517 1520 b'web', b'server-header', default=None,
1518 1521 )
1519 1522 coreconfigitem(
1520 1523 b'web', b'static', default=None,
1521 1524 )
1522 1525 coreconfigitem(
1523 1526 b'web', b'staticurl', default=None,
1524 1527 )
1525 1528 coreconfigitem(
1526 1529 b'web', b'stripes', default=1,
1527 1530 )
1528 1531 coreconfigitem(
1529 1532 b'web', b'style', default=b'paper',
1530 1533 )
1531 1534 coreconfigitem(
1532 1535 b'web', b'templates', default=None,
1533 1536 )
1534 1537 coreconfigitem(
1535 1538 b'web', b'view', default=b'served', experimental=True,
1536 1539 )
1537 1540 coreconfigitem(
1538 1541 b'worker', b'backgroundclose', default=dynamicdefault,
1539 1542 )
1540 1543 # Windows defaults to a limit of 512 open files. A buffer of 128
1541 1544 # should give us enough headway.
1542 1545 coreconfigitem(
1543 1546 b'worker', b'backgroundclosemaxqueue', default=384,
1544 1547 )
1545 1548 coreconfigitem(
1546 1549 b'worker', b'backgroundcloseminfilecount', default=2048,
1547 1550 )
1548 1551 coreconfigitem(
1549 1552 b'worker', b'backgroundclosethreadcount', default=4,
1550 1553 )
1551 1554 coreconfigitem(
1552 1555 b'worker', b'enabled', default=True,
1553 1556 )
1554 1557 coreconfigitem(
1555 1558 b'worker', b'numcpus', default=None,
1556 1559 )
1557 1560
1558 1561 # Rebase related configuration moved to core because other extension are doing
1559 1562 # strange things. For example, shelve import the extensions to reuse some bit
1560 1563 # without formally loading it.
1561 1564 coreconfigitem(
1562 1565 b'commands', b'rebase.requiredest', default=False,
1563 1566 )
1564 1567 coreconfigitem(
1565 1568 b'experimental', b'rebaseskipobsolete', default=True,
1566 1569 )
1567 1570 coreconfigitem(
1568 1571 b'rebase', b'singletransaction', default=False,
1569 1572 )
1570 1573 coreconfigitem(
1571 1574 b'rebase', b'experimental.inmemory', default=False,
1572 1575 )
@@ -1,2335 +1,2351 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 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 collections
11 11 import contextlib
12 import datetime
12 13 import errno
13 14 import getpass
14 15 import inspect
15 16 import os
16 17 import re
17 18 import signal
18 19 import socket
19 20 import subprocess
20 21 import sys
21 22 import traceback
22 23
23 24 from .i18n import _
24 25 from .node import hex
25 26 from .pycompat import (
26 27 getattr,
27 28 open,
28 29 setattr,
29 30 )
30 31
31 32 from . import (
32 33 color,
33 34 config,
34 35 configitems,
35 36 encoding,
36 37 error,
37 38 formatter,
38 39 loggingutil,
39 40 progress,
40 41 pycompat,
41 42 rcutil,
42 43 scmutil,
43 44 util,
44 45 )
45 46 from .utils import (
46 47 dateutil,
47 48 procutil,
48 49 resourceutil,
49 50 stringutil,
50 51 )
51 52
52 53 urlreq = util.urlreq
53 54
54 55 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
55 56 _keepalnum = b''.join(
56 57 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
57 58 )
58 59
59 60 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
60 61 tweakrc = b"""
61 62 [ui]
62 63 # The rollback command is dangerous. As a rule, don't use it.
63 64 rollback = False
64 65 # Make `hg status` report copy information
65 66 statuscopies = yes
66 67 # Prefer curses UIs when available. Revert to plain-text with `text`.
67 68 interface = curses
68 69 # Make compatible commands emit cwd-relative paths by default.
69 70 relative-paths = yes
70 71
71 72 [commands]
72 73 # Grep working directory by default.
73 74 grep.all-files = True
74 75 # Refuse to perform an `hg update` that would cause a file content merge
75 76 update.check = noconflict
76 77 # Show conflicts information in `hg status`
77 78 status.verbose = True
78 79 # Make `hg resolve` with no action (like `-m`) fail instead of re-merging.
79 80 resolve.explicit-re-merge = True
80 81
81 82 [diff]
82 83 git = 1
83 84 showfunc = 1
84 85 word-diff = 1
85 86 """
86 87
87 88 samplehgrcs = {
88 89 b'user': b"""# example user config (see 'hg help config' for more info)
89 90 [ui]
90 91 # name and email, e.g.
91 92 # username = Jane Doe <jdoe@example.com>
92 93 username =
93 94
94 95 # We recommend enabling tweakdefaults to get slight improvements to
95 96 # the UI over time. Make sure to set HGPLAIN in the environment when
96 97 # writing scripts!
97 98 # tweakdefaults = True
98 99
99 100 # uncomment to disable color in command output
100 101 # (see 'hg help color' for details)
101 102 # color = never
102 103
103 104 # uncomment to disable command output pagination
104 105 # (see 'hg help pager' for details)
105 106 # paginate = never
106 107
107 108 [extensions]
108 109 # uncomment the lines below to enable some popular extensions
109 110 # (see 'hg help extensions' for more info)
110 111 #
111 112 # histedit =
112 113 # rebase =
113 114 # uncommit =
114 115 """,
115 116 b'cloned': b"""# example repository config (see 'hg help config' for more info)
116 117 [paths]
117 118 default = %s
118 119
119 120 # path aliases to other clones of this repo in URLs or filesystem paths
120 121 # (see 'hg help config.paths' for more info)
121 122 #
122 123 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
123 124 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
124 125 # my-clone = /home/jdoe/jdoes-clone
125 126
126 127 [ui]
127 128 # name and email (local to this repository, optional), e.g.
128 129 # username = Jane Doe <jdoe@example.com>
129 130 """,
130 131 b'local': b"""# example repository config (see 'hg help config' for more info)
131 132 [paths]
132 133 # path aliases to other clones of this repo in URLs or filesystem paths
133 134 # (see 'hg help config.paths' for more info)
134 135 #
135 136 # default = http://example.com/hg/example-repo
136 137 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
137 138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
138 139 # my-clone = /home/jdoe/jdoes-clone
139 140
140 141 [ui]
141 142 # name and email (local to this repository, optional), e.g.
142 143 # username = Jane Doe <jdoe@example.com>
143 144 """,
144 145 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
145 146
146 147 [ui]
147 148 # uncomment to disable color in command output
148 149 # (see 'hg help color' for details)
149 150 # color = never
150 151
151 152 # uncomment to disable command output pagination
152 153 # (see 'hg help pager' for details)
153 154 # paginate = never
154 155
155 156 [extensions]
156 157 # uncomment the lines below to enable some popular extensions
157 158 # (see 'hg help extensions' for more info)
158 159 #
159 160 # blackbox =
160 161 # churn =
161 162 """,
162 163 }
163 164
164 165
165 166 def _maybestrurl(maybebytes):
166 167 return pycompat.rapply(pycompat.strurl, maybebytes)
167 168
168 169
169 170 def _maybebytesurl(maybestr):
170 171 return pycompat.rapply(pycompat.bytesurl, maybestr)
171 172
172 173
173 174 class httppasswordmgrdbproxy(object):
174 175 """Delays loading urllib2 until it's needed."""
175 176
176 177 def __init__(self):
177 178 self._mgr = None
178 179
179 180 def _get_mgr(self):
180 181 if self._mgr is None:
181 182 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
182 183 return self._mgr
183 184
184 185 def add_password(self, realm, uris, user, passwd):
185 186 return self._get_mgr().add_password(
186 187 _maybestrurl(realm),
187 188 _maybestrurl(uris),
188 189 _maybestrurl(user),
189 190 _maybestrurl(passwd),
190 191 )
191 192
192 193 def find_user_password(self, realm, uri):
193 194 mgr = self._get_mgr()
194 195 return _maybebytesurl(
195 196 mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
196 197 )
197 198
198 199
199 200 def _catchterm(*args):
200 201 raise error.SignalInterrupt
201 202
202 203
203 204 # unique object used to detect no default value has been provided when
204 205 # retrieving configuration value.
205 206 _unset = object()
206 207
207 208 # _reqexithandlers: callbacks run at the end of a request
208 209 _reqexithandlers = []
209 210
210 211
211 212 class ui(object):
212 213 def __init__(self, src=None):
213 214 """Create a fresh new ui object if no src given
214 215
215 216 Use uimod.ui.load() to create a ui which knows global and user configs.
216 217 In most cases, you should use ui.copy() to create a copy of an existing
217 218 ui object.
218 219 """
219 220 # _buffers: used for temporary capture of output
220 221 self._buffers = []
221 222 # 3-tuple describing how each buffer in the stack behaves.
222 223 # Values are (capture stderr, capture subprocesses, apply labels).
223 224 self._bufferstates = []
224 225 # When a buffer is active, defines whether we are expanding labels.
225 226 # This exists to prevent an extra list lookup.
226 227 self._bufferapplylabels = None
227 228 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
228 229 self._reportuntrusted = True
229 230 self._knownconfig = configitems.coreitems
230 231 self._ocfg = config.config() # overlay
231 232 self._tcfg = config.config() # trusted
232 233 self._ucfg = config.config() # untrusted
233 234 self._trustusers = set()
234 235 self._trustgroups = set()
235 236 self.callhooks = True
236 237 # Insecure server connections requested.
237 238 self.insecureconnections = False
238 239 # Blocked time
239 240 self.logblockedtimes = False
240 241 # color mode: see mercurial/color.py for possible value
241 242 self._colormode = None
242 243 self._terminfoparams = {}
243 244 self._styles = {}
244 245 self._uninterruptible = False
246 self.showtimestamp = False
245 247
246 248 if src:
247 249 self._fout = src._fout
248 250 self._ferr = src._ferr
249 251 self._fin = src._fin
250 252 self._fmsg = src._fmsg
251 253 self._fmsgout = src._fmsgout
252 254 self._fmsgerr = src._fmsgerr
253 255 self._finoutredirected = src._finoutredirected
254 256 self._loggers = src._loggers.copy()
255 257 self.pageractive = src.pageractive
256 258 self._disablepager = src._disablepager
257 259 self._tweaked = src._tweaked
258 260
259 261 self._tcfg = src._tcfg.copy()
260 262 self._ucfg = src._ucfg.copy()
261 263 self._ocfg = src._ocfg.copy()
262 264 self._trustusers = src._trustusers.copy()
263 265 self._trustgroups = src._trustgroups.copy()
264 266 self.environ = src.environ
265 267 self.callhooks = src.callhooks
266 268 self.insecureconnections = src.insecureconnections
267 269 self._colormode = src._colormode
268 270 self._terminfoparams = src._terminfoparams.copy()
269 271 self._styles = src._styles.copy()
270 272
271 273 self.fixconfig()
272 274
273 275 self.httppasswordmgrdb = src.httppasswordmgrdb
274 276 self._blockedtimes = src._blockedtimes
275 277 else:
276 278 self._fout = procutil.stdout
277 279 self._ferr = procutil.stderr
278 280 self._fin = procutil.stdin
279 281 self._fmsg = None
280 282 self._fmsgout = self.fout # configurable
281 283 self._fmsgerr = self.ferr # configurable
282 284 self._finoutredirected = False
283 285 self._loggers = {}
284 286 self.pageractive = False
285 287 self._disablepager = False
286 288 self._tweaked = False
287 289
288 290 # shared read-only environment
289 291 self.environ = encoding.environ
290 292
291 293 self.httppasswordmgrdb = httppasswordmgrdbproxy()
292 294 self._blockedtimes = collections.defaultdict(int)
293 295
294 296 allowed = self.configlist(b'experimental', b'exportableenviron')
295 297 if b'*' in allowed:
296 298 self._exportableenviron = self.environ
297 299 else:
298 300 self._exportableenviron = {}
299 301 for k in allowed:
300 302 if k in self.environ:
301 303 self._exportableenviron[k] = self.environ[k]
302 304
303 305 @classmethod
304 306 def load(cls):
305 307 """Create a ui and load global and user configs"""
306 308 u = cls()
307 309 # we always trust global config files and environment variables
308 310 for t, f in rcutil.rccomponents():
309 311 if t == b'path':
310 312 u.readconfig(f, trust=True)
311 313 elif t == b'resource':
312 314 u.read_resource_config(f, trust=True)
313 315 elif t == b'items':
314 316 sections = set()
315 317 for section, name, value, source in f:
316 318 # do not set u._ocfg
317 319 # XXX clean this up once immutable config object is a thing
318 320 u._tcfg.set(section, name, value, source)
319 321 u._ucfg.set(section, name, value, source)
320 322 sections.add(section)
321 323 for section in sections:
322 324 u.fixconfig(section=section)
323 325 else:
324 326 raise error.ProgrammingError(b'unknown rctype: %s' % t)
325 327 u._maybetweakdefaults()
326 328 return u
327 329
328 330 def _maybetweakdefaults(self):
329 331 if not self.configbool(b'ui', b'tweakdefaults'):
330 332 return
331 333 if self._tweaked or self.plain(b'tweakdefaults'):
332 334 return
333 335
334 336 # Note: it is SUPER IMPORTANT that you set self._tweaked to
335 337 # True *before* any calls to setconfig(), otherwise you'll get
336 338 # infinite recursion between setconfig and this method.
337 339 #
338 340 # TODO: We should extract an inner method in setconfig() to
339 341 # avoid this weirdness.
340 342 self._tweaked = True
341 343 tmpcfg = config.config()
342 344 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
343 345 for section in tmpcfg:
344 346 for name, value in tmpcfg.items(section):
345 347 if not self.hasconfig(section, name):
346 348 self.setconfig(section, name, value, b"<tweakdefaults>")
347 349
348 350 def copy(self):
349 351 return self.__class__(self)
350 352
351 353 def resetstate(self):
352 354 """Clear internal state that shouldn't persist across commands"""
353 355 if self._progbar:
354 356 self._progbar.resetstate() # reset last-print time of progress bar
355 357 self.httppasswordmgrdb = httppasswordmgrdbproxy()
356 358
357 359 @contextlib.contextmanager
358 360 def timeblockedsection(self, key):
359 361 # this is open-coded below - search for timeblockedsection to find them
360 362 starttime = util.timer()
361 363 try:
362 364 yield
363 365 finally:
364 366 self._blockedtimes[key + b'_blocked'] += (
365 367 util.timer() - starttime
366 368 ) * 1000
367 369
368 370 @contextlib.contextmanager
369 371 def uninterruptible(self):
370 372 """Mark an operation as unsafe.
371 373
372 374 Most operations on a repository are safe to interrupt, but a
373 375 few are risky (for example repair.strip). This context manager
374 376 lets you advise Mercurial that something risky is happening so
375 377 that control-C etc can be blocked if desired.
376 378 """
377 379 enabled = self.configbool(b'experimental', b'nointerrupt')
378 380 if enabled and self.configbool(
379 381 b'experimental', b'nointerrupt-interactiveonly'
380 382 ):
381 383 enabled = self.interactive()
382 384 if self._uninterruptible or not enabled:
383 385 # if nointerrupt support is turned off, the process isn't
384 386 # interactive, or we're already in an uninterruptible
385 387 # block, do nothing.
386 388 yield
387 389 return
388 390
389 391 def warn():
390 392 self.warn(_(b"shutting down cleanly\n"))
391 393 self.warn(
392 394 _(b"press ^C again to terminate immediately (dangerous)\n")
393 395 )
394 396 return True
395 397
396 398 with procutil.uninterruptible(warn):
397 399 try:
398 400 self._uninterruptible = True
399 401 yield
400 402 finally:
401 403 self._uninterruptible = False
402 404
403 405 def formatter(self, topic, opts):
404 406 return formatter.formatter(self, self, topic, opts)
405 407
406 408 def _trusted(self, fp, f):
407 409 st = util.fstat(fp)
408 410 if util.isowner(st):
409 411 return True
410 412
411 413 tusers, tgroups = self._trustusers, self._trustgroups
412 414 if b'*' in tusers or b'*' in tgroups:
413 415 return True
414 416
415 417 user = util.username(st.st_uid)
416 418 group = util.groupname(st.st_gid)
417 419 if user in tusers or group in tgroups or user == util.username():
418 420 return True
419 421
420 422 if self._reportuntrusted:
421 423 self.warn(
422 424 _(
423 425 b'not trusting file %s from untrusted '
424 426 b'user %s, group %s\n'
425 427 )
426 428 % (f, user, group)
427 429 )
428 430 return False
429 431
430 432 def read_resource_config(
431 433 self, name, root=None, trust=False, sections=None, remap=None
432 434 ):
433 435 try:
434 436 fp = resourceutil.open_resource(name[0], name[1])
435 437 except IOError:
436 438 if not sections: # ignore unless we were looking for something
437 439 return
438 440 raise
439 441
440 442 self._readconfig(
441 443 b'resource:%s.%s' % name, fp, root, trust, sections, remap
442 444 )
443 445
444 446 def readconfig(
445 447 self, filename, root=None, trust=False, sections=None, remap=None
446 448 ):
447 449 try:
448 450 fp = open(filename, 'rb')
449 451 except IOError:
450 452 if not sections: # ignore unless we were looking for something
451 453 return
452 454 raise
453 455
454 456 self._readconfig(filename, fp, root, trust, sections, remap)
455 457
456 458 def _readconfig(
457 459 self, filename, fp, root=None, trust=False, sections=None, remap=None
458 460 ):
459 461 with fp:
460 462 cfg = config.config()
461 463 trusted = sections or trust or self._trusted(fp, filename)
462 464
463 465 try:
464 466 cfg.read(filename, fp, sections=sections, remap=remap)
465 467 except error.ParseError as inst:
466 468 if trusted:
467 469 raise
468 470 self.warn(_(b'ignored: %s\n') % stringutil.forcebytestr(inst))
469 471
470 472 self._applyconfig(cfg, trusted, root)
471 473
472 474 def applyconfig(self, configitems, source=b"", root=None):
473 475 """Add configitems from a non-file source. Unlike with ``setconfig()``,
474 476 they can be overridden by subsequent config file reads. The items are
475 477 in the same format as ``configoverride()``, namely a dict of the
476 478 following structures: {(section, name) : value}
477 479
478 480 Typically this is used by extensions that inject themselves into the
479 481 config file load procedure by monkeypatching ``localrepo.loadhgrc()``.
480 482 """
481 483 cfg = config.config()
482 484
483 485 for (section, name), value in configitems.items():
484 486 cfg.set(section, name, value, source)
485 487
486 488 self._applyconfig(cfg, True, root)
487 489
488 490 def _applyconfig(self, cfg, trusted, root):
489 491 if self.plain():
490 492 for k in (
491 493 b'debug',
492 494 b'fallbackencoding',
493 495 b'quiet',
494 496 b'slash',
495 497 b'logtemplate',
496 498 b'message-output',
497 499 b'statuscopies',
498 500 b'style',
499 501 b'traceback',
500 502 b'verbose',
501 503 ):
502 504 if k in cfg[b'ui']:
503 505 del cfg[b'ui'][k]
504 506 for k, v in cfg.items(b'defaults'):
505 507 del cfg[b'defaults'][k]
506 508 for k, v in cfg.items(b'commands'):
507 509 del cfg[b'commands'][k]
508 510 # Don't remove aliases from the configuration if in the exceptionlist
509 511 if self.plain(b'alias'):
510 512 for k, v in cfg.items(b'alias'):
511 513 del cfg[b'alias'][k]
512 514 if self.plain(b'revsetalias'):
513 515 for k, v in cfg.items(b'revsetalias'):
514 516 del cfg[b'revsetalias'][k]
515 517 if self.plain(b'templatealias'):
516 518 for k, v in cfg.items(b'templatealias'):
517 519 del cfg[b'templatealias'][k]
518 520
519 521 if trusted:
520 522 self._tcfg.update(cfg)
521 523 self._tcfg.update(self._ocfg)
522 524 self._ucfg.update(cfg)
523 525 self._ucfg.update(self._ocfg)
524 526
525 527 if root is None:
526 528 root = os.path.expanduser(b'~')
527 529 self.fixconfig(root=root)
528 530
529 531 def fixconfig(self, root=None, section=None):
530 532 if section in (None, b'paths'):
531 533 # expand vars and ~
532 534 # translate paths relative to root (or home) into absolute paths
533 535 root = root or encoding.getcwd()
534 536 for c in self._tcfg, self._ucfg, self._ocfg:
535 537 for n, p in c.items(b'paths'):
536 538 # Ignore sub-options.
537 539 if b':' in n:
538 540 continue
539 541 if not p:
540 542 continue
541 543 if b'%%' in p:
542 544 s = self.configsource(b'paths', n) or b'none'
543 545 self.warn(
544 546 _(b"(deprecated '%%' in path %s=%s from %s)\n")
545 547 % (n, p, s)
546 548 )
547 549 p = p.replace(b'%%', b'%')
548 550 p = util.expandpath(p)
549 551 if not util.hasscheme(p) and not os.path.isabs(p):
550 552 p = os.path.normpath(os.path.join(root, p))
551 553 c.set(b"paths", n, p)
552 554
553 555 if section in (None, b'ui'):
554 556 # update ui options
555 557 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
556 558 self.debugflag = self.configbool(b'ui', b'debug')
557 559 self.verbose = self.debugflag or self.configbool(b'ui', b'verbose')
558 560 self.quiet = not self.debugflag and self.configbool(b'ui', b'quiet')
559 561 if self.verbose and self.quiet:
560 562 self.quiet = self.verbose = False
561 563 self._reportuntrusted = self.debugflag or self.configbool(
562 564 b"ui", b"report_untrusted"
563 565 )
566 self.showtimestamp = self.configbool(b'ui', b'timestamp-output')
564 567 self.tracebackflag = self.configbool(b'ui', b'traceback')
565 568 self.logblockedtimes = self.configbool(b'ui', b'logblockedtimes')
566 569
567 570 if section in (None, b'trusted'):
568 571 # update trust information
569 572 self._trustusers.update(self.configlist(b'trusted', b'users'))
570 573 self._trustgroups.update(self.configlist(b'trusted', b'groups'))
571 574
572 575 if section in (None, b'devel', b'ui') and self.debugflag:
573 576 tracked = set()
574 577 if self.configbool(b'devel', b'debug.extensions'):
575 578 tracked.add(b'extension')
576 579 if tracked:
577 580 logger = loggingutil.fileobjectlogger(self._ferr, tracked)
578 581 self.setlogger(b'debug', logger)
579 582
580 583 def backupconfig(self, section, item):
581 584 return (
582 585 self._ocfg.backup(section, item),
583 586 self._tcfg.backup(section, item),
584 587 self._ucfg.backup(section, item),
585 588 )
586 589
587 590 def restoreconfig(self, data):
588 591 self._ocfg.restore(data[0])
589 592 self._tcfg.restore(data[1])
590 593 self._ucfg.restore(data[2])
591 594
592 595 def setconfig(self, section, name, value, source=b''):
593 596 for cfg in (self._ocfg, self._tcfg, self._ucfg):
594 597 cfg.set(section, name, value, source)
595 598 self.fixconfig(section=section)
596 599 self._maybetweakdefaults()
597 600
598 601 def _data(self, untrusted):
599 602 return untrusted and self._ucfg or self._tcfg
600 603
601 604 def configsource(self, section, name, untrusted=False):
602 605 return self._data(untrusted).source(section, name)
603 606
604 607 def config(self, section, name, default=_unset, untrusted=False):
605 608 """return the plain string version of a config"""
606 609 value = self._config(
607 610 section, name, default=default, untrusted=untrusted
608 611 )
609 612 if value is _unset:
610 613 return None
611 614 return value
612 615
613 616 def _config(self, section, name, default=_unset, untrusted=False):
614 617 value = itemdefault = default
615 618 item = self._knownconfig.get(section, {}).get(name)
616 619 alternates = [(section, name)]
617 620
618 621 if item is not None:
619 622 alternates.extend(item.alias)
620 623 if callable(item.default):
621 624 itemdefault = item.default()
622 625 else:
623 626 itemdefault = item.default
624 627 else:
625 628 msg = b"accessing unregistered config item: '%s.%s'"
626 629 msg %= (section, name)
627 630 self.develwarn(msg, 2, b'warn-config-unknown')
628 631
629 632 if default is _unset:
630 633 if item is None:
631 634 value = default
632 635 elif item.default is configitems.dynamicdefault:
633 636 value = None
634 637 msg = b"config item requires an explicit default value: '%s.%s'"
635 638 msg %= (section, name)
636 639 self.develwarn(msg, 2, b'warn-config-default')
637 640 else:
638 641 value = itemdefault
639 642 elif (
640 643 item is not None
641 644 and item.default is not configitems.dynamicdefault
642 645 and default != itemdefault
643 646 ):
644 647 msg = (
645 648 b"specifying a mismatched default value for a registered "
646 649 b"config item: '%s.%s' '%s'"
647 650 )
648 651 msg %= (section, name, pycompat.bytestr(default))
649 652 self.develwarn(msg, 2, b'warn-config-default')
650 653
651 654 for s, n in alternates:
652 655 candidate = self._data(untrusted).get(s, n, None)
653 656 if candidate is not None:
654 657 value = candidate
655 658 break
656 659
657 660 if self.debugflag and not untrusted and self._reportuntrusted:
658 661 for s, n in alternates:
659 662 uvalue = self._ucfg.get(s, n)
660 663 if uvalue is not None and uvalue != value:
661 664 self.debug(
662 665 b"ignoring untrusted configuration option "
663 666 b"%s.%s = %s\n" % (s, n, uvalue)
664 667 )
665 668 return value
666 669
667 670 def configsuboptions(self, section, name, default=_unset, untrusted=False):
668 671 """Get a config option and all sub-options.
669 672
670 673 Some config options have sub-options that are declared with the
671 674 format "key:opt = value". This method is used to return the main
672 675 option and all its declared sub-options.
673 676
674 677 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
675 678 is a dict of defined sub-options where keys and values are strings.
676 679 """
677 680 main = self.config(section, name, default, untrusted=untrusted)
678 681 data = self._data(untrusted)
679 682 sub = {}
680 683 prefix = b'%s:' % name
681 684 for k, v in data.items(section):
682 685 if k.startswith(prefix):
683 686 sub[k[len(prefix) :]] = v
684 687
685 688 if self.debugflag and not untrusted and self._reportuntrusted:
686 689 for k, v in sub.items():
687 690 uvalue = self._ucfg.get(section, b'%s:%s' % (name, k))
688 691 if uvalue is not None and uvalue != v:
689 692 self.debug(
690 693 b'ignoring untrusted configuration option '
691 694 b'%s:%s.%s = %s\n' % (section, name, k, uvalue)
692 695 )
693 696
694 697 return main, sub
695 698
696 699 def configpath(self, section, name, default=_unset, untrusted=False):
697 700 """get a path config item, expanded relative to repo root or config
698 701 file"""
699 702 v = self.config(section, name, default, untrusted)
700 703 if v is None:
701 704 return None
702 705 if not os.path.isabs(v) or b"://" not in v:
703 706 src = self.configsource(section, name, untrusted)
704 707 if b':' in src:
705 708 base = os.path.dirname(src.rsplit(b':')[0])
706 709 v = os.path.join(base, os.path.expanduser(v))
707 710 return v
708 711
709 712 def configbool(self, section, name, default=_unset, untrusted=False):
710 713 """parse a configuration element as a boolean
711 714
712 715 >>> u = ui(); s = b'foo'
713 716 >>> u.setconfig(s, b'true', b'yes')
714 717 >>> u.configbool(s, b'true')
715 718 True
716 719 >>> u.setconfig(s, b'false', b'no')
717 720 >>> u.configbool(s, b'false')
718 721 False
719 722 >>> u.configbool(s, b'unknown')
720 723 False
721 724 >>> u.configbool(s, b'unknown', True)
722 725 True
723 726 >>> u.setconfig(s, b'invalid', b'somevalue')
724 727 >>> u.configbool(s, b'invalid')
725 728 Traceback (most recent call last):
726 729 ...
727 730 ConfigError: foo.invalid is not a boolean ('somevalue')
728 731 """
729 732
730 733 v = self._config(section, name, default, untrusted=untrusted)
731 734 if v is None:
732 735 return v
733 736 if v is _unset:
734 737 if default is _unset:
735 738 return False
736 739 return default
737 740 if isinstance(v, bool):
738 741 return v
739 742 b = stringutil.parsebool(v)
740 743 if b is None:
741 744 raise error.ConfigError(
742 745 _(b"%s.%s is not a boolean ('%s')") % (section, name, v)
743 746 )
744 747 return b
745 748
746 749 def configwith(
747 750 self, convert, section, name, default=_unset, desc=None, untrusted=False
748 751 ):
749 752 """parse a configuration element with a conversion function
750 753
751 754 >>> u = ui(); s = b'foo'
752 755 >>> u.setconfig(s, b'float1', b'42')
753 756 >>> u.configwith(float, s, b'float1')
754 757 42.0
755 758 >>> u.setconfig(s, b'float2', b'-4.25')
756 759 >>> u.configwith(float, s, b'float2')
757 760 -4.25
758 761 >>> u.configwith(float, s, b'unknown', 7)
759 762 7.0
760 763 >>> u.setconfig(s, b'invalid', b'somevalue')
761 764 >>> u.configwith(float, s, b'invalid')
762 765 Traceback (most recent call last):
763 766 ...
764 767 ConfigError: foo.invalid is not a valid float ('somevalue')
765 768 >>> u.configwith(float, s, b'invalid', desc=b'womble')
766 769 Traceback (most recent call last):
767 770 ...
768 771 ConfigError: foo.invalid is not a valid womble ('somevalue')
769 772 """
770 773
771 774 v = self.config(section, name, default, untrusted)
772 775 if v is None:
773 776 return v # do not attempt to convert None
774 777 try:
775 778 return convert(v)
776 779 except (ValueError, error.ParseError):
777 780 if desc is None:
778 781 desc = pycompat.sysbytes(convert.__name__)
779 782 raise error.ConfigError(
780 783 _(b"%s.%s is not a valid %s ('%s')") % (section, name, desc, v)
781 784 )
782 785
783 786 def configint(self, section, name, default=_unset, untrusted=False):
784 787 """parse a configuration element as an integer
785 788
786 789 >>> u = ui(); s = b'foo'
787 790 >>> u.setconfig(s, b'int1', b'42')
788 791 >>> u.configint(s, b'int1')
789 792 42
790 793 >>> u.setconfig(s, b'int2', b'-42')
791 794 >>> u.configint(s, b'int2')
792 795 -42
793 796 >>> u.configint(s, b'unknown', 7)
794 797 7
795 798 >>> u.setconfig(s, b'invalid', b'somevalue')
796 799 >>> u.configint(s, b'invalid')
797 800 Traceback (most recent call last):
798 801 ...
799 802 ConfigError: foo.invalid is not a valid integer ('somevalue')
800 803 """
801 804
802 805 return self.configwith(
803 806 int, section, name, default, b'integer', untrusted
804 807 )
805 808
806 809 def configbytes(self, section, name, default=_unset, untrusted=False):
807 810 """parse a configuration element as a quantity in bytes
808 811
809 812 Units can be specified as b (bytes), k or kb (kilobytes), m or
810 813 mb (megabytes), g or gb (gigabytes).
811 814
812 815 >>> u = ui(); s = b'foo'
813 816 >>> u.setconfig(s, b'val1', b'42')
814 817 >>> u.configbytes(s, b'val1')
815 818 42
816 819 >>> u.setconfig(s, b'val2', b'42.5 kb')
817 820 >>> u.configbytes(s, b'val2')
818 821 43520
819 822 >>> u.configbytes(s, b'unknown', b'7 MB')
820 823 7340032
821 824 >>> u.setconfig(s, b'invalid', b'somevalue')
822 825 >>> u.configbytes(s, b'invalid')
823 826 Traceback (most recent call last):
824 827 ...
825 828 ConfigError: foo.invalid is not a byte quantity ('somevalue')
826 829 """
827 830
828 831 value = self._config(section, name, default, untrusted)
829 832 if value is _unset:
830 833 if default is _unset:
831 834 default = 0
832 835 value = default
833 836 if not isinstance(value, bytes):
834 837 return value
835 838 try:
836 839 return util.sizetoint(value)
837 840 except error.ParseError:
838 841 raise error.ConfigError(
839 842 _(b"%s.%s is not a byte quantity ('%s')")
840 843 % (section, name, value)
841 844 )
842 845
843 846 def configlist(self, section, name, default=_unset, untrusted=False):
844 847 """parse a configuration element as a list of comma/space separated
845 848 strings
846 849
847 850 >>> u = ui(); s = b'foo'
848 851 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
849 852 >>> u.configlist(s, b'list1')
850 853 ['this', 'is', 'a small', 'test']
851 854 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
852 855 >>> u.configlist(s, b'list2')
853 856 ['this', 'is', 'a small', 'test']
854 857 """
855 858 # default is not always a list
856 859 v = self.configwith(
857 860 config.parselist, section, name, default, b'list', untrusted
858 861 )
859 862 if isinstance(v, bytes):
860 863 return config.parselist(v)
861 864 elif v is None:
862 865 return []
863 866 return v
864 867
865 868 def configdate(self, section, name, default=_unset, untrusted=False):
866 869 """parse a configuration element as a tuple of ints
867 870
868 871 >>> u = ui(); s = b'foo'
869 872 >>> u.setconfig(s, b'date', b'0 0')
870 873 >>> u.configdate(s, b'date')
871 874 (0, 0)
872 875 """
873 876 if self.config(section, name, default, untrusted):
874 877 return self.configwith(
875 878 dateutil.parsedate, section, name, default, b'date', untrusted
876 879 )
877 880 if default is _unset:
878 881 return None
879 882 return default
880 883
881 884 def configdefault(self, section, name):
882 885 """returns the default value of the config item"""
883 886 item = self._knownconfig.get(section, {}).get(name)
884 887 itemdefault = None
885 888 if item is not None:
886 889 if callable(item.default):
887 890 itemdefault = item.default()
888 891 else:
889 892 itemdefault = item.default
890 893 return itemdefault
891 894
892 895 def hasconfig(self, section, name, untrusted=False):
893 896 return self._data(untrusted).hasitem(section, name)
894 897
895 898 def has_section(self, section, untrusted=False):
896 899 '''tell whether section exists in config.'''
897 900 return section in self._data(untrusted)
898 901
899 902 def configitems(self, section, untrusted=False, ignoresub=False):
900 903 items = self._data(untrusted).items(section)
901 904 if ignoresub:
902 905 items = [i for i in items if b':' not in i[0]]
903 906 if self.debugflag and not untrusted and self._reportuntrusted:
904 907 for k, v in self._ucfg.items(section):
905 908 if self._tcfg.get(section, k) != v:
906 909 self.debug(
907 910 b"ignoring untrusted configuration option "
908 911 b"%s.%s = %s\n" % (section, k, v)
909 912 )
910 913 return items
911 914
912 915 def walkconfig(self, untrusted=False):
913 916 cfg = self._data(untrusted)
914 917 for section in cfg.sections():
915 918 for name, value in self.configitems(section, untrusted):
916 919 yield section, name, value
917 920
918 921 def plain(self, feature=None):
919 922 '''is plain mode active?
920 923
921 924 Plain mode means that all configuration variables which affect
922 925 the behavior and output of Mercurial should be
923 926 ignored. Additionally, the output should be stable,
924 927 reproducible and suitable for use in scripts or applications.
925 928
926 929 The only way to trigger plain mode is by setting either the
927 930 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
928 931
929 932 The return value can either be
930 933 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
931 934 - False if feature is disabled by default and not included in HGPLAIN
932 935 - True otherwise
933 936 '''
934 937 if (
935 938 b'HGPLAIN' not in encoding.environ
936 939 and b'HGPLAINEXCEPT' not in encoding.environ
937 940 ):
938 941 return False
939 942 exceptions = (
940 943 encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
941 944 )
942 945 # TODO: add support for HGPLAIN=+feature,-feature syntax
943 946 if b'+strictflags' not in encoding.environ.get(b'HGPLAIN', b'').split(
944 947 b','
945 948 ):
946 949 exceptions.append(b'strictflags')
947 950 if feature and exceptions:
948 951 return feature not in exceptions
949 952 return True
950 953
951 954 def username(self, acceptempty=False):
952 955 """Return default username to be used in commits.
953 956
954 957 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
955 958 and stop searching if one of these is set.
956 959 If not found and acceptempty is True, returns None.
957 960 If not found and ui.askusername is True, ask the user, else use
958 961 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
959 962 If no username could be found, raise an Abort error.
960 963 """
961 964 user = encoding.environ.get(b"HGUSER")
962 965 if user is None:
963 966 user = self.config(b"ui", b"username")
964 967 if user is not None:
965 968 user = os.path.expandvars(user)
966 969 if user is None:
967 970 user = encoding.environ.get(b"EMAIL")
968 971 if user is None and acceptempty:
969 972 return user
970 973 if user is None and self.configbool(b"ui", b"askusername"):
971 974 user = self.prompt(_(b"enter a commit username:"), default=None)
972 975 if user is None and not self.interactive():
973 976 try:
974 977 user = b'%s@%s' % (
975 978 procutil.getuser(),
976 979 encoding.strtolocal(socket.getfqdn()),
977 980 )
978 981 self.warn(_(b"no username found, using '%s' instead\n") % user)
979 982 except KeyError:
980 983 pass
981 984 if not user:
982 985 raise error.Abort(
983 986 _(b'no username supplied'),
984 987 hint=_(b"use 'hg config --edit' " b'to set your username'),
985 988 )
986 989 if b"\n" in user:
987 990 raise error.Abort(
988 991 _(b"username %r contains a newline\n") % pycompat.bytestr(user)
989 992 )
990 993 return user
991 994
992 995 def shortuser(self, user):
993 996 """Return a short representation of a user name or email address."""
994 997 if not self.verbose:
995 998 user = stringutil.shortuser(user)
996 999 return user
997 1000
998 1001 def expandpath(self, loc, default=None):
999 1002 """Return repository location relative to cwd or from [paths]"""
1000 1003 try:
1001 1004 p = self.paths.getpath(loc)
1002 1005 if p:
1003 1006 return p.rawloc
1004 1007 except error.RepoError:
1005 1008 pass
1006 1009
1007 1010 if default:
1008 1011 try:
1009 1012 p = self.paths.getpath(default)
1010 1013 if p:
1011 1014 return p.rawloc
1012 1015 except error.RepoError:
1013 1016 pass
1014 1017
1015 1018 return loc
1016 1019
1017 1020 @util.propertycache
1018 1021 def paths(self):
1019 1022 return paths(self)
1020 1023
1021 1024 @property
1022 1025 def fout(self):
1023 1026 return self._fout
1024 1027
1025 1028 @fout.setter
1026 1029 def fout(self, f):
1027 1030 self._fout = f
1028 1031 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1029 1032
1030 1033 @property
1031 1034 def ferr(self):
1032 1035 return self._ferr
1033 1036
1034 1037 @ferr.setter
1035 1038 def ferr(self, f):
1036 1039 self._ferr = f
1037 1040 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1038 1041
1039 1042 @property
1040 1043 def fin(self):
1041 1044 return self._fin
1042 1045
1043 1046 @fin.setter
1044 1047 def fin(self, f):
1045 1048 self._fin = f
1046 1049
1047 1050 @property
1048 1051 def fmsg(self):
1049 1052 """Stream dedicated for status/error messages; may be None if
1050 1053 fout/ferr are used"""
1051 1054 return self._fmsg
1052 1055
1053 1056 @fmsg.setter
1054 1057 def fmsg(self, f):
1055 1058 self._fmsg = f
1056 1059 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1057 1060
1058 1061 def pushbuffer(self, error=False, subproc=False, labeled=False):
1059 1062 """install a buffer to capture standard output of the ui object
1060 1063
1061 1064 If error is True, the error output will be captured too.
1062 1065
1063 1066 If subproc is True, output from subprocesses (typically hooks) will be
1064 1067 captured too.
1065 1068
1066 1069 If labeled is True, any labels associated with buffered
1067 1070 output will be handled. By default, this has no effect
1068 1071 on the output returned, but extensions and GUI tools may
1069 1072 handle this argument and returned styled output. If output
1070 1073 is being buffered so it can be captured and parsed or
1071 1074 processed, labeled should not be set to True.
1072 1075 """
1073 1076 self._buffers.append([])
1074 1077 self._bufferstates.append((error, subproc, labeled))
1075 1078 self._bufferapplylabels = labeled
1076 1079
1077 1080 def popbuffer(self):
1078 1081 '''pop the last buffer and return the buffered output'''
1079 1082 self._bufferstates.pop()
1080 1083 if self._bufferstates:
1081 1084 self._bufferapplylabels = self._bufferstates[-1][2]
1082 1085 else:
1083 1086 self._bufferapplylabels = None
1084 1087
1085 1088 return b"".join(self._buffers.pop())
1086 1089
1087 1090 def _isbuffered(self, dest):
1088 1091 if dest is self._fout:
1089 1092 return bool(self._buffers)
1090 1093 if dest is self._ferr:
1091 1094 return bool(self._bufferstates and self._bufferstates[-1][0])
1092 1095 return False
1093 1096
1094 1097 def canwritewithoutlabels(self):
1095 1098 '''check if write skips the label'''
1096 1099 if self._buffers and not self._bufferapplylabels:
1097 1100 return True
1098 1101 return self._colormode is None
1099 1102
1100 1103 def canbatchlabeledwrites(self):
1101 1104 '''check if write calls with labels are batchable'''
1102 1105 # Windows color printing is special, see ``write``.
1103 1106 return self._colormode != b'win32'
1104 1107
1105 1108 def write(self, *args, **opts):
1106 1109 '''write args to output
1107 1110
1108 1111 By default, this method simply writes to the buffer or stdout.
1109 1112 Color mode can be set on the UI class to have the output decorated
1110 1113 with color modifier before being written to stdout.
1111 1114
1112 1115 The color used is controlled by an optional keyword argument, "label".
1113 1116 This should be a string containing label names separated by space.
1114 1117 Label names take the form of "topic.type". For example, ui.debug()
1115 1118 issues a label of "ui.debug".
1116 1119
1117 1120 Progress reports via stderr are normally cleared before writing as
1118 1121 stdout and stderr go to the same terminal. This can be skipped with
1119 1122 the optional keyword argument "keepprogressbar". The progress bar
1120 1123 will continue to occupy a partial line on stderr in that case.
1121 1124 This functionality is intended when Mercurial acts as data source
1122 1125 in a pipe.
1123 1126
1124 1127 When labeling output for a specific command, a label of
1125 1128 "cmdname.type" is recommended. For example, status issues
1126 1129 a label of "status.modified" for modified files.
1127 1130 '''
1128 1131 dest = self._fout
1129 1132
1130 1133 # inlined _write() for speed
1131 1134 if self._buffers:
1132 1135 label = opts.get('label', b'')
1133 1136 if label and self._bufferapplylabels:
1134 1137 self._buffers[-1].extend(self.label(a, label) for a in args)
1135 1138 else:
1136 1139 self._buffers[-1].extend(args)
1137 1140 return
1138 1141
1139 1142 # inlined _writenobuf() for speed
1140 1143 if not opts.get('keepprogressbar', False):
1141 1144 self._progclear()
1142 1145 msg = b''.join(args)
1143 1146
1144 1147 # opencode timeblockedsection because this is a critical path
1145 1148 starttime = util.timer()
1146 1149 try:
1147 1150 if self._colormode == b'win32':
1148 1151 # windows color printing is its own can of crab, defer to
1149 1152 # the color module and that is it.
1150 1153 color.win32print(self, dest.write, msg, **opts)
1151 1154 else:
1152 1155 if self._colormode is not None:
1153 1156 label = opts.get('label', b'')
1154 1157 msg = self.label(msg, label)
1155 1158 dest.write(msg)
1156 1159 except IOError as err:
1157 1160 raise error.StdioError(err)
1158 1161 finally:
1159 1162 self._blockedtimes[b'stdio_blocked'] += (
1160 1163 util.timer() - starttime
1161 1164 ) * 1000
1162 1165
1163 1166 def write_err(self, *args, **opts):
1164 1167 self._write(self._ferr, *args, **opts)
1165 1168
1166 1169 def _write(self, dest, *args, **opts):
1167 1170 # update write() as well if you touch this code
1168 1171 if self._isbuffered(dest):
1169 1172 label = opts.get('label', b'')
1170 1173 if label and self._bufferapplylabels:
1171 1174 self._buffers[-1].extend(self.label(a, label) for a in args)
1172 1175 else:
1173 1176 self._buffers[-1].extend(args)
1174 1177 else:
1175 1178 self._writenobuf(dest, *args, **opts)
1176 1179
1177 1180 def _writenobuf(self, dest, *args, **opts):
1178 1181 # update write() as well if you touch this code
1179 1182 if not opts.get('keepprogressbar', False):
1180 1183 self._progclear()
1181 1184 msg = b''.join(args)
1182 1185
1183 1186 # opencode timeblockedsection because this is a critical path
1184 1187 starttime = util.timer()
1185 1188 try:
1186 1189 if dest is self._ferr and not getattr(self._fout, 'closed', False):
1187 1190 self._fout.flush()
1188 1191 if getattr(dest, 'structured', False):
1189 1192 # channel for machine-readable output with metadata, where
1190 1193 # no extra colorization is necessary.
1191 1194 dest.write(msg, **opts)
1192 1195 elif self._colormode == b'win32':
1193 1196 # windows color printing is its own can of crab, defer to
1194 1197 # the color module and that is it.
1195 1198 color.win32print(self, dest.write, msg, **opts)
1196 1199 else:
1197 1200 if self._colormode is not None:
1198 1201 label = opts.get('label', b'')
1199 1202 msg = self.label(msg, label)
1200 1203 dest.write(msg)
1201 1204 # stderr may be buffered under win32 when redirected to files,
1202 1205 # including stdout.
1203 1206 if dest is self._ferr and not getattr(dest, 'closed', False):
1204 1207 dest.flush()
1205 1208 except IOError as err:
1206 1209 if dest is self._ferr and err.errno in (
1207 1210 errno.EPIPE,
1208 1211 errno.EIO,
1209 1212 errno.EBADF,
1210 1213 ):
1211 1214 # no way to report the error, so ignore it
1212 1215 return
1213 1216 raise error.StdioError(err)
1214 1217 finally:
1215 1218 self._blockedtimes[b'stdio_blocked'] += (
1216 1219 util.timer() - starttime
1217 1220 ) * 1000
1218 1221
1219 1222 def _writemsg(self, dest, *args, **opts):
1223 timestamp = self.showtimestamp and opts.get('type') in {
1224 b'debug',
1225 b'error',
1226 b'note',
1227 b'status',
1228 b'warning',
1229 }
1230 if timestamp:
1231 args = (
1232 b'[%s] ' % bytes(datetime.datetime.now().isoformat(), 'ASCII'),
1233 ) + args
1220 1234 _writemsgwith(self._write, dest, *args, **opts)
1235 if timestamp:
1236 dest.flush()
1221 1237
1222 1238 def _writemsgnobuf(self, dest, *args, **opts):
1223 1239 _writemsgwith(self._writenobuf, dest, *args, **opts)
1224 1240
1225 1241 def flush(self):
1226 1242 # opencode timeblockedsection because this is a critical path
1227 1243 starttime = util.timer()
1228 1244 try:
1229 1245 try:
1230 1246 self._fout.flush()
1231 1247 except IOError as err:
1232 1248 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1233 1249 raise error.StdioError(err)
1234 1250 finally:
1235 1251 try:
1236 1252 self._ferr.flush()
1237 1253 except IOError as err:
1238 1254 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1239 1255 raise error.StdioError(err)
1240 1256 finally:
1241 1257 self._blockedtimes[b'stdio_blocked'] += (
1242 1258 util.timer() - starttime
1243 1259 ) * 1000
1244 1260
1245 1261 def _isatty(self, fh):
1246 1262 if self.configbool(b'ui', b'nontty'):
1247 1263 return False
1248 1264 return procutil.isatty(fh)
1249 1265
1250 1266 def protectfinout(self):
1251 1267 """Duplicate ui streams and redirect original if they are stdio
1252 1268
1253 1269 Returns (fin, fout) which point to the original ui fds, but may be
1254 1270 copy of them. The returned streams can be considered "owned" in that
1255 1271 print(), exec(), etc. never reach to them.
1256 1272 """
1257 1273 if self._finoutredirected:
1258 1274 # if already redirected, protectstdio() would just create another
1259 1275 # nullfd pair, which is equivalent to returning self._fin/_fout.
1260 1276 return self._fin, self._fout
1261 1277 fin, fout = procutil.protectstdio(self._fin, self._fout)
1262 1278 self._finoutredirected = (fin, fout) != (self._fin, self._fout)
1263 1279 return fin, fout
1264 1280
1265 1281 def restorefinout(self, fin, fout):
1266 1282 """Restore ui streams from possibly duplicated (fin, fout)"""
1267 1283 if (fin, fout) == (self._fin, self._fout):
1268 1284 return
1269 1285 procutil.restorestdio(self._fin, self._fout, fin, fout)
1270 1286 # protectfinout() won't create more than one duplicated streams,
1271 1287 # so we can just turn the redirection flag off.
1272 1288 self._finoutredirected = False
1273 1289
1274 1290 @contextlib.contextmanager
1275 1291 def protectedfinout(self):
1276 1292 """Run code block with protected standard streams"""
1277 1293 fin, fout = self.protectfinout()
1278 1294 try:
1279 1295 yield fin, fout
1280 1296 finally:
1281 1297 self.restorefinout(fin, fout)
1282 1298
1283 1299 def disablepager(self):
1284 1300 self._disablepager = True
1285 1301
1286 1302 def pager(self, command):
1287 1303 """Start a pager for subsequent command output.
1288 1304
1289 1305 Commands which produce a long stream of output should call
1290 1306 this function to activate the user's preferred pagination
1291 1307 mechanism (which may be no pager). Calling this function
1292 1308 precludes any future use of interactive functionality, such as
1293 1309 prompting the user or activating curses.
1294 1310
1295 1311 Args:
1296 1312 command: The full, non-aliased name of the command. That is, "log"
1297 1313 not "history, "summary" not "summ", etc.
1298 1314 """
1299 1315 if self._disablepager or self.pageractive:
1300 1316 # how pager should do is already determined
1301 1317 return
1302 1318
1303 1319 if not command.startswith(b'internal-always-') and (
1304 1320 # explicit --pager=on (= 'internal-always-' prefix) should
1305 1321 # take precedence over disabling factors below
1306 1322 command in self.configlist(b'pager', b'ignore')
1307 1323 or not self.configbool(b'ui', b'paginate')
1308 1324 or not self.configbool(b'pager', b'attend-' + command, True)
1309 1325 or encoding.environ.get(b'TERM') == b'dumb'
1310 1326 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1311 1327 # formatted() will need some adjustment.
1312 1328 or not self.formatted()
1313 1329 or self.plain()
1314 1330 or self._buffers
1315 1331 # TODO: expose debugger-enabled on the UI object
1316 1332 or b'--debugger' in pycompat.sysargv
1317 1333 ):
1318 1334 # We only want to paginate if the ui appears to be
1319 1335 # interactive, the user didn't say HGPLAIN or
1320 1336 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1321 1337 return
1322 1338
1323 1339 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1324 1340 if not pagercmd:
1325 1341 return
1326 1342
1327 1343 pagerenv = {}
1328 1344 for name, value in rcutil.defaultpagerenv().items():
1329 1345 if name not in encoding.environ:
1330 1346 pagerenv[name] = value
1331 1347
1332 1348 self.debug(
1333 1349 b'starting pager for command %s\n' % stringutil.pprint(command)
1334 1350 )
1335 1351 self.flush()
1336 1352
1337 1353 wasformatted = self.formatted()
1338 1354 if util.safehasattr(signal, b"SIGPIPE"):
1339 1355 signal.signal(signal.SIGPIPE, _catchterm)
1340 1356 if self._runpager(pagercmd, pagerenv):
1341 1357 self.pageractive = True
1342 1358 # Preserve the formatted-ness of the UI. This is important
1343 1359 # because we mess with stdout, which might confuse
1344 1360 # auto-detection of things being formatted.
1345 1361 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1346 1362 self.setconfig(b'ui', b'interactive', False, b'pager')
1347 1363
1348 1364 # If pagermode differs from color.mode, reconfigure color now that
1349 1365 # pageractive is set.
1350 1366 cm = self._colormode
1351 1367 if cm != self.config(b'color', b'pagermode', cm):
1352 1368 color.setup(self)
1353 1369 else:
1354 1370 # If the pager can't be spawned in dispatch when --pager=on is
1355 1371 # given, don't try again when the command runs, to avoid a duplicate
1356 1372 # warning about a missing pager command.
1357 1373 self.disablepager()
1358 1374
1359 1375 def _runpager(self, command, env=None):
1360 1376 """Actually start the pager and set up file descriptors.
1361 1377
1362 1378 This is separate in part so that extensions (like chg) can
1363 1379 override how a pager is invoked.
1364 1380 """
1365 1381 if command == b'cat':
1366 1382 # Save ourselves some work.
1367 1383 return False
1368 1384 # If the command doesn't contain any of these characters, we
1369 1385 # assume it's a binary and exec it directly. This means for
1370 1386 # simple pager command configurations, we can degrade
1371 1387 # gracefully and tell the user about their broken pager.
1372 1388 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1373 1389
1374 1390 if pycompat.iswindows and not shell:
1375 1391 # Window's built-in `more` cannot be invoked with shell=False, but
1376 1392 # its `more.com` can. Hide this implementation detail from the
1377 1393 # user so we can also get sane bad PAGER behavior. MSYS has
1378 1394 # `more.exe`, so do a cmd.exe style resolution of the executable to
1379 1395 # determine which one to use.
1380 1396 fullcmd = procutil.findexe(command)
1381 1397 if not fullcmd:
1382 1398 self.warn(
1383 1399 _(b"missing pager command '%s', skipping pager\n") % command
1384 1400 )
1385 1401 return False
1386 1402
1387 1403 command = fullcmd
1388 1404
1389 1405 try:
1390 1406 pager = subprocess.Popen(
1391 1407 procutil.tonativestr(command),
1392 1408 shell=shell,
1393 1409 bufsize=-1,
1394 1410 close_fds=procutil.closefds,
1395 1411 stdin=subprocess.PIPE,
1396 1412 stdout=procutil.stdout,
1397 1413 stderr=procutil.stderr,
1398 1414 env=procutil.tonativeenv(procutil.shellenviron(env)),
1399 1415 )
1400 1416 except OSError as e:
1401 1417 if e.errno == errno.ENOENT and not shell:
1402 1418 self.warn(
1403 1419 _(b"missing pager command '%s', skipping pager\n") % command
1404 1420 )
1405 1421 return False
1406 1422 raise
1407 1423
1408 1424 # back up original file descriptors
1409 1425 stdoutfd = os.dup(procutil.stdout.fileno())
1410 1426 stderrfd = os.dup(procutil.stderr.fileno())
1411 1427
1412 1428 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1413 1429 if self._isatty(procutil.stderr):
1414 1430 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1415 1431
1416 1432 @self.atexit
1417 1433 def killpager():
1418 1434 if util.safehasattr(signal, b"SIGINT"):
1419 1435 signal.signal(signal.SIGINT, signal.SIG_IGN)
1420 1436 # restore original fds, closing pager.stdin copies in the process
1421 1437 os.dup2(stdoutfd, procutil.stdout.fileno())
1422 1438 os.dup2(stderrfd, procutil.stderr.fileno())
1423 1439 pager.stdin.close()
1424 1440 pager.wait()
1425 1441
1426 1442 return True
1427 1443
1428 1444 @property
1429 1445 def _exithandlers(self):
1430 1446 return _reqexithandlers
1431 1447
1432 1448 def atexit(self, func, *args, **kwargs):
1433 1449 '''register a function to run after dispatching a request
1434 1450
1435 1451 Handlers do not stay registered across request boundaries.'''
1436 1452 self._exithandlers.append((func, args, kwargs))
1437 1453 return func
1438 1454
1439 1455 def interface(self, feature):
1440 1456 """what interface to use for interactive console features?
1441 1457
1442 1458 The interface is controlled by the value of `ui.interface` but also by
1443 1459 the value of feature-specific configuration. For example:
1444 1460
1445 1461 ui.interface.histedit = text
1446 1462 ui.interface.chunkselector = curses
1447 1463
1448 1464 Here the features are "histedit" and "chunkselector".
1449 1465
1450 1466 The configuration above means that the default interfaces for commands
1451 1467 is curses, the interface for histedit is text and the interface for
1452 1468 selecting chunk is crecord (the best curses interface available).
1453 1469
1454 1470 Consider the following example:
1455 1471 ui.interface = curses
1456 1472 ui.interface.histedit = text
1457 1473
1458 1474 Then histedit will use the text interface and chunkselector will use
1459 1475 the default curses interface (crecord at the moment).
1460 1476 """
1461 1477 alldefaults = frozenset([b"text", b"curses"])
1462 1478
1463 1479 featureinterfaces = {
1464 1480 b"chunkselector": [b"text", b"curses",],
1465 1481 b"histedit": [b"text", b"curses",],
1466 1482 }
1467 1483
1468 1484 # Feature-specific interface
1469 1485 if feature not in featureinterfaces.keys():
1470 1486 # Programming error, not user error
1471 1487 raise ValueError(b"Unknown feature requested %s" % feature)
1472 1488
1473 1489 availableinterfaces = frozenset(featureinterfaces[feature])
1474 1490 if alldefaults > availableinterfaces:
1475 1491 # Programming error, not user error. We need a use case to
1476 1492 # define the right thing to do here.
1477 1493 raise ValueError(
1478 1494 b"Feature %s does not handle all default interfaces" % feature
1479 1495 )
1480 1496
1481 1497 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1482 1498 return b"text"
1483 1499
1484 1500 # Default interface for all the features
1485 1501 defaultinterface = b"text"
1486 1502 i = self.config(b"ui", b"interface")
1487 1503 if i in alldefaults:
1488 1504 defaultinterface = i
1489 1505
1490 1506 choseninterface = defaultinterface
1491 1507 f = self.config(b"ui", b"interface.%s" % feature)
1492 1508 if f in availableinterfaces:
1493 1509 choseninterface = f
1494 1510
1495 1511 if i is not None and defaultinterface != i:
1496 1512 if f is not None:
1497 1513 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1498 1514 else:
1499 1515 self.warn(
1500 1516 _(b"invalid value for ui.interface: %s (using %s)\n")
1501 1517 % (i, choseninterface)
1502 1518 )
1503 1519 if f is not None and choseninterface != f:
1504 1520 self.warn(
1505 1521 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1506 1522 % (feature, f, choseninterface)
1507 1523 )
1508 1524
1509 1525 return choseninterface
1510 1526
1511 1527 def interactive(self):
1512 1528 '''is interactive input allowed?
1513 1529
1514 1530 An interactive session is a session where input can be reasonably read
1515 1531 from `sys.stdin'. If this function returns false, any attempt to read
1516 1532 from stdin should fail with an error, unless a sensible default has been
1517 1533 specified.
1518 1534
1519 1535 Interactiveness is triggered by the value of the `ui.interactive'
1520 1536 configuration variable or - if it is unset - when `sys.stdin' points
1521 1537 to a terminal device.
1522 1538
1523 1539 This function refers to input only; for output, see `ui.formatted()'.
1524 1540 '''
1525 1541 i = self.configbool(b"ui", b"interactive")
1526 1542 if i is None:
1527 1543 # some environments replace stdin without implementing isatty
1528 1544 # usually those are non-interactive
1529 1545 return self._isatty(self._fin)
1530 1546
1531 1547 return i
1532 1548
1533 1549 def termwidth(self):
1534 1550 '''how wide is the terminal in columns?
1535 1551 '''
1536 1552 if b'COLUMNS' in encoding.environ:
1537 1553 try:
1538 1554 return int(encoding.environ[b'COLUMNS'])
1539 1555 except ValueError:
1540 1556 pass
1541 1557 return scmutil.termsize(self)[0]
1542 1558
1543 1559 def formatted(self):
1544 1560 '''should formatted output be used?
1545 1561
1546 1562 It is often desirable to format the output to suite the output medium.
1547 1563 Examples of this are truncating long lines or colorizing messages.
1548 1564 However, this is not often not desirable when piping output into other
1549 1565 utilities, e.g. `grep'.
1550 1566
1551 1567 Formatted output is triggered by the value of the `ui.formatted'
1552 1568 configuration variable or - if it is unset - when `sys.stdout' points
1553 1569 to a terminal device. Please note that `ui.formatted' should be
1554 1570 considered an implementation detail; it is not intended for use outside
1555 1571 Mercurial or its extensions.
1556 1572
1557 1573 This function refers to output only; for input, see `ui.interactive()'.
1558 1574 This function always returns false when in plain mode, see `ui.plain()'.
1559 1575 '''
1560 1576 if self.plain():
1561 1577 return False
1562 1578
1563 1579 i = self.configbool(b"ui", b"formatted")
1564 1580 if i is None:
1565 1581 # some environments replace stdout without implementing isatty
1566 1582 # usually those are non-interactive
1567 1583 return self._isatty(self._fout)
1568 1584
1569 1585 return i
1570 1586
1571 1587 def _readline(self, prompt=b' ', promptopts=None):
1572 1588 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1573 1589 # because they have to be text streams with *no buffering*. Instead,
1574 1590 # we use rawinput() only if call_readline() will be invoked by
1575 1591 # PyOS_Readline(), so no I/O will be made at Python layer.
1576 1592 usereadline = (
1577 1593 self._isatty(self._fin)
1578 1594 and self._isatty(self._fout)
1579 1595 and procutil.isstdin(self._fin)
1580 1596 and procutil.isstdout(self._fout)
1581 1597 )
1582 1598 if usereadline:
1583 1599 try:
1584 1600 # magically add command line editing support, where
1585 1601 # available
1586 1602 import readline
1587 1603
1588 1604 # force demandimport to really load the module
1589 1605 readline.read_history_file
1590 1606 # windows sometimes raises something other than ImportError
1591 1607 except Exception:
1592 1608 usereadline = False
1593 1609
1594 1610 if self._colormode == b'win32' or not usereadline:
1595 1611 if not promptopts:
1596 1612 promptopts = {}
1597 1613 self._writemsgnobuf(
1598 1614 self._fmsgout, prompt, type=b'prompt', **promptopts
1599 1615 )
1600 1616 self.flush()
1601 1617 prompt = b' '
1602 1618 else:
1603 1619 prompt = self.label(prompt, b'ui.prompt') + b' '
1604 1620
1605 1621 # prompt ' ' must exist; otherwise readline may delete entire line
1606 1622 # - http://bugs.python.org/issue12833
1607 1623 with self.timeblockedsection(b'stdio'):
1608 1624 if usereadline:
1609 1625 self.flush()
1610 1626 prompt = encoding.strfromlocal(prompt)
1611 1627 line = encoding.strtolocal(pycompat.rawinput(prompt))
1612 1628 # When stdin is in binary mode on Windows, it can cause
1613 1629 # raw_input() to emit an extra trailing carriage return
1614 1630 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1615 1631 line = line[:-1]
1616 1632 else:
1617 1633 self._fout.write(pycompat.bytestr(prompt))
1618 1634 self._fout.flush()
1619 1635 line = self._fin.readline()
1620 1636 if not line:
1621 1637 raise EOFError
1622 1638 line = line.rstrip(pycompat.oslinesep)
1623 1639
1624 1640 return line
1625 1641
1626 1642 def prompt(self, msg, default=b"y"):
1627 1643 """Prompt user with msg, read response.
1628 1644 If ui is not interactive, the default is returned.
1629 1645 """
1630 1646 return self._prompt(msg, default=default)
1631 1647
1632 1648 def _prompt(self, msg, **opts):
1633 1649 default = opts['default']
1634 1650 if not self.interactive():
1635 1651 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1636 1652 self._writemsg(
1637 1653 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1638 1654 )
1639 1655 return default
1640 1656 try:
1641 1657 r = self._readline(prompt=msg, promptopts=opts)
1642 1658 if not r:
1643 1659 r = default
1644 1660 if self.configbool(b'ui', b'promptecho'):
1645 1661 self._writemsg(self._fmsgout, r, b"\n", type=b'promptecho')
1646 1662 return r
1647 1663 except EOFError:
1648 1664 raise error.ResponseExpected()
1649 1665
1650 1666 @staticmethod
1651 1667 def extractchoices(prompt):
1652 1668 """Extract prompt message and list of choices from specified prompt.
1653 1669
1654 1670 This returns tuple "(message, choices)", and "choices" is the
1655 1671 list of tuple "(response character, text without &)".
1656 1672
1657 1673 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1658 1674 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1659 1675 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1660 1676 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1661 1677 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1662 1678 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1663 1679 """
1664 1680
1665 1681 # Sadly, the prompt string may have been built with a filename
1666 1682 # containing "$$" so let's try to find the first valid-looking
1667 1683 # prompt to start parsing. Sadly, we also can't rely on
1668 1684 # choices containing spaces, ASCII, or basically anything
1669 1685 # except an ampersand followed by a character.
1670 1686 m = re.match(br'(?s)(.+?)\$\$([^$]*&[^ $].*)', prompt)
1671 1687 msg = m.group(1)
1672 1688 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1673 1689
1674 1690 def choicetuple(s):
1675 1691 ampidx = s.index(b'&')
1676 1692 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1677 1693
1678 1694 return (msg, [choicetuple(s) for s in choices])
1679 1695
1680 1696 def promptchoice(self, prompt, default=0):
1681 1697 """Prompt user with a message, read response, and ensure it matches
1682 1698 one of the provided choices. The prompt is formatted as follows:
1683 1699
1684 1700 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1685 1701
1686 1702 The index of the choice is returned. Responses are case
1687 1703 insensitive. If ui is not interactive, the default is
1688 1704 returned.
1689 1705 """
1690 1706
1691 1707 msg, choices = self.extractchoices(prompt)
1692 1708 resps = [r for r, t in choices]
1693 1709 while True:
1694 1710 r = self._prompt(msg, default=resps[default], choices=choices)
1695 1711 if r.lower() in resps:
1696 1712 return resps.index(r.lower())
1697 1713 # TODO: shouldn't it be a warning?
1698 1714 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1699 1715
1700 1716 def getpass(self, prompt=None, default=None):
1701 1717 if not self.interactive():
1702 1718 return default
1703 1719 try:
1704 1720 self._writemsg(
1705 1721 self._fmsgerr,
1706 1722 prompt or _(b'password: '),
1707 1723 type=b'prompt',
1708 1724 password=True,
1709 1725 )
1710 1726 # disable getpass() only if explicitly specified. it's still valid
1711 1727 # to interact with tty even if fin is not a tty.
1712 1728 with self.timeblockedsection(b'stdio'):
1713 1729 if self.configbool(b'ui', b'nontty'):
1714 1730 l = self._fin.readline()
1715 1731 if not l:
1716 1732 raise EOFError
1717 1733 return l.rstrip(b'\n')
1718 1734 else:
1719 1735 return getpass.getpass('')
1720 1736 except EOFError:
1721 1737 raise error.ResponseExpected()
1722 1738
1723 1739 def status(self, *msg, **opts):
1724 1740 '''write status message to output (if ui.quiet is False)
1725 1741
1726 1742 This adds an output label of "ui.status".
1727 1743 '''
1728 1744 if not self.quiet:
1729 1745 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1730 1746
1731 1747 def warn(self, *msg, **opts):
1732 1748 '''write warning message to output (stderr)
1733 1749
1734 1750 This adds an output label of "ui.warning".
1735 1751 '''
1736 1752 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1737 1753
1738 1754 def error(self, *msg, **opts):
1739 1755 '''write error message to output (stderr)
1740 1756
1741 1757 This adds an output label of "ui.error".
1742 1758 '''
1743 1759 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1744 1760
1745 1761 def note(self, *msg, **opts):
1746 1762 '''write note to output (if ui.verbose is True)
1747 1763
1748 1764 This adds an output label of "ui.note".
1749 1765 '''
1750 1766 if self.verbose:
1751 1767 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1752 1768
1753 1769 def debug(self, *msg, **opts):
1754 1770 '''write debug message to output (if ui.debugflag is True)
1755 1771
1756 1772 This adds an output label of "ui.debug".
1757 1773 '''
1758 1774 if self.debugflag:
1759 1775 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1760 1776 self.log(b'debug', b'%s', b''.join(msg))
1761 1777
1762 1778 # Aliases to defeat check-code.
1763 1779 statusnoi18n = status
1764 1780 notenoi18n = note
1765 1781 warnnoi18n = warn
1766 1782 writenoi18n = write
1767 1783
1768 1784 def edit(
1769 1785 self,
1770 1786 text,
1771 1787 user,
1772 1788 extra=None,
1773 1789 editform=None,
1774 1790 pending=None,
1775 1791 repopath=None,
1776 1792 action=None,
1777 1793 ):
1778 1794 if action is None:
1779 1795 self.develwarn(
1780 1796 b'action is None but will soon be a required '
1781 1797 b'parameter to ui.edit()'
1782 1798 )
1783 1799 extra_defaults = {
1784 1800 b'prefix': b'editor',
1785 1801 b'suffix': b'.txt',
1786 1802 }
1787 1803 if extra is not None:
1788 1804 if extra.get(b'suffix') is not None:
1789 1805 self.develwarn(
1790 1806 b'extra.suffix is not None but will soon be '
1791 1807 b'ignored by ui.edit()'
1792 1808 )
1793 1809 extra_defaults.update(extra)
1794 1810 extra = extra_defaults
1795 1811
1796 1812 if action == b'diff':
1797 1813 suffix = b'.diff'
1798 1814 elif action:
1799 1815 suffix = b'.%s.hg.txt' % action
1800 1816 else:
1801 1817 suffix = extra[b'suffix']
1802 1818
1803 1819 rdir = None
1804 1820 if self.configbool(b'experimental', b'editortmpinhg'):
1805 1821 rdir = repopath
1806 1822 (fd, name) = pycompat.mkstemp(
1807 1823 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1808 1824 )
1809 1825 try:
1810 1826 with os.fdopen(fd, 'wb') as f:
1811 1827 f.write(util.tonativeeol(text))
1812 1828
1813 1829 environ = {b'HGUSER': user}
1814 1830 if b'transplant_source' in extra:
1815 1831 environ.update(
1816 1832 {b'HGREVISION': hex(extra[b'transplant_source'])}
1817 1833 )
1818 1834 for label in (b'intermediate-source', b'source', b'rebase_source'):
1819 1835 if label in extra:
1820 1836 environ.update({b'HGREVISION': extra[label]})
1821 1837 break
1822 1838 if editform:
1823 1839 environ.update({b'HGEDITFORM': editform})
1824 1840 if pending:
1825 1841 environ.update({b'HG_PENDING': pending})
1826 1842
1827 1843 editor = self.geteditor()
1828 1844
1829 1845 self.system(
1830 1846 b"%s \"%s\"" % (editor, name),
1831 1847 environ=environ,
1832 1848 onerr=error.Abort,
1833 1849 errprefix=_(b"edit failed"),
1834 1850 blockedtag=b'editor',
1835 1851 )
1836 1852
1837 1853 with open(name, 'rb') as f:
1838 1854 t = util.fromnativeeol(f.read())
1839 1855 finally:
1840 1856 os.unlink(name)
1841 1857
1842 1858 return t
1843 1859
1844 1860 def system(
1845 1861 self,
1846 1862 cmd,
1847 1863 environ=None,
1848 1864 cwd=None,
1849 1865 onerr=None,
1850 1866 errprefix=None,
1851 1867 blockedtag=None,
1852 1868 ):
1853 1869 '''execute shell command with appropriate output stream. command
1854 1870 output will be redirected if fout is not stdout.
1855 1871
1856 1872 if command fails and onerr is None, return status, else raise onerr
1857 1873 object as exception.
1858 1874 '''
1859 1875 if blockedtag is None:
1860 1876 # Long cmds tend to be because of an absolute path on cmd. Keep
1861 1877 # the tail end instead
1862 1878 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1863 1879 blockedtag = b'unknown_system_' + cmdsuffix
1864 1880 out = self._fout
1865 1881 if any(s[1] for s in self._bufferstates):
1866 1882 out = self
1867 1883 with self.timeblockedsection(blockedtag):
1868 1884 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1869 1885 if rc and onerr:
1870 1886 errmsg = b'%s %s' % (
1871 1887 procutil.shellsplit(cmd)[0],
1872 1888 procutil.explainexit(rc),
1873 1889 )
1874 1890 if errprefix:
1875 1891 errmsg = b'%s: %s' % (errprefix, errmsg)
1876 1892 raise onerr(errmsg)
1877 1893 return rc
1878 1894
1879 1895 def _runsystem(self, cmd, environ, cwd, out):
1880 1896 """actually execute the given shell command (can be overridden by
1881 1897 extensions like chg)"""
1882 1898 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1883 1899
1884 1900 def traceback(self, exc=None, force=False):
1885 1901 '''print exception traceback if traceback printing enabled or forced.
1886 1902 only to call in exception handler. returns true if traceback
1887 1903 printed.'''
1888 1904 if self.tracebackflag or force:
1889 1905 if exc is None:
1890 1906 exc = sys.exc_info()
1891 1907 cause = getattr(exc[1], 'cause', None)
1892 1908
1893 1909 if cause is not None:
1894 1910 causetb = traceback.format_tb(cause[2])
1895 1911 exctb = traceback.format_tb(exc[2])
1896 1912 exconly = traceback.format_exception_only(cause[0], cause[1])
1897 1913
1898 1914 # exclude frame where 'exc' was chained and rethrown from exctb
1899 1915 self.write_err(
1900 1916 b'Traceback (most recent call last):\n',
1901 1917 encoding.strtolocal(''.join(exctb[:-1])),
1902 1918 encoding.strtolocal(''.join(causetb)),
1903 1919 encoding.strtolocal(''.join(exconly)),
1904 1920 )
1905 1921 else:
1906 1922 output = traceback.format_exception(exc[0], exc[1], exc[2])
1907 1923 self.write_err(encoding.strtolocal(''.join(output)))
1908 1924 return self.tracebackflag or force
1909 1925
1910 1926 def geteditor(self):
1911 1927 '''return editor to use'''
1912 1928 if pycompat.sysplatform == b'plan9':
1913 1929 # vi is the MIPS instruction simulator on Plan 9. We
1914 1930 # instead default to E to plumb commit messages to
1915 1931 # avoid confusion.
1916 1932 editor = b'E'
1917 1933 elif pycompat.isdarwin:
1918 1934 # vi on darwin is POSIX compatible to a fault, and that includes
1919 1935 # exiting non-zero if you make any mistake when running an ex
1920 1936 # command. Proof: `vi -c ':unknown' -c ':qa'; echo $?` produces 1,
1921 1937 # while s/vi/vim/ doesn't.
1922 1938 editor = b'vim'
1923 1939 else:
1924 1940 editor = b'vi'
1925 1941 return encoding.environ.get(b"HGEDITOR") or self.config(
1926 1942 b"ui", b"editor", editor
1927 1943 )
1928 1944
1929 1945 @util.propertycache
1930 1946 def _progbar(self):
1931 1947 """setup the progbar singleton to the ui object"""
1932 1948 if (
1933 1949 self.quiet
1934 1950 or self.debugflag
1935 1951 or self.configbool(b'progress', b'disable')
1936 1952 or not progress.shouldprint(self)
1937 1953 ):
1938 1954 return None
1939 1955 return getprogbar(self)
1940 1956
1941 1957 def _progclear(self):
1942 1958 """clear progress bar output if any. use it before any output"""
1943 1959 if not haveprogbar(): # nothing loaded yet
1944 1960 return
1945 1961 if self._progbar is not None and self._progbar.printed:
1946 1962 self._progbar.clear()
1947 1963
1948 1964 def makeprogress(self, topic, unit=b"", total=None):
1949 1965 """Create a progress helper for the specified topic"""
1950 1966 if getattr(self._fmsgerr, 'structured', False):
1951 1967 # channel for machine-readable output with metadata, just send
1952 1968 # raw information
1953 1969 # TODO: consider porting some useful information (e.g. estimated
1954 1970 # time) from progbar. we might want to support update delay to
1955 1971 # reduce the cost of transferring progress messages.
1956 1972 def updatebar(topic, pos, item, unit, total):
1957 1973 self._fmsgerr.write(
1958 1974 None,
1959 1975 type=b'progress',
1960 1976 topic=topic,
1961 1977 pos=pos,
1962 1978 item=item,
1963 1979 unit=unit,
1964 1980 total=total,
1965 1981 )
1966 1982
1967 1983 elif self._progbar is not None:
1968 1984 updatebar = self._progbar.progress
1969 1985 else:
1970 1986
1971 1987 def updatebar(topic, pos, item, unit, total):
1972 1988 pass
1973 1989
1974 1990 return scmutil.progress(self, updatebar, topic, unit, total)
1975 1991
1976 1992 def getlogger(self, name):
1977 1993 """Returns a logger of the given name; or None if not registered"""
1978 1994 return self._loggers.get(name)
1979 1995
1980 1996 def setlogger(self, name, logger):
1981 1997 """Install logger which can be identified later by the given name
1982 1998
1983 1999 More than one loggers can be registered. Use extension or module
1984 2000 name to uniquely identify the logger instance.
1985 2001 """
1986 2002 self._loggers[name] = logger
1987 2003
1988 2004 def log(self, event, msgfmt, *msgargs, **opts):
1989 2005 '''hook for logging facility extensions
1990 2006
1991 2007 event should be a readily-identifiable subsystem, which will
1992 2008 allow filtering.
1993 2009
1994 2010 msgfmt should be a newline-terminated format string to log, and
1995 2011 *msgargs are %-formatted into it.
1996 2012
1997 2013 **opts currently has no defined meanings.
1998 2014 '''
1999 2015 if not self._loggers:
2000 2016 return
2001 2017 activeloggers = [
2002 2018 l for l in pycompat.itervalues(self._loggers) if l.tracked(event)
2003 2019 ]
2004 2020 if not activeloggers:
2005 2021 return
2006 2022 msg = msgfmt % msgargs
2007 2023 opts = pycompat.byteskwargs(opts)
2008 2024 # guard against recursion from e.g. ui.debug()
2009 2025 registeredloggers = self._loggers
2010 2026 self._loggers = {}
2011 2027 try:
2012 2028 for logger in activeloggers:
2013 2029 logger.log(self, event, msg, opts)
2014 2030 finally:
2015 2031 self._loggers = registeredloggers
2016 2032
2017 2033 def label(self, msg, label):
2018 2034 '''style msg based on supplied label
2019 2035
2020 2036 If some color mode is enabled, this will add the necessary control
2021 2037 characters to apply such color. In addition, 'debug' color mode adds
2022 2038 markup showing which label affects a piece of text.
2023 2039
2024 2040 ui.write(s, 'label') is equivalent to
2025 2041 ui.write(ui.label(s, 'label')).
2026 2042 '''
2027 2043 if self._colormode is not None:
2028 2044 return color.colorlabel(self, msg, label)
2029 2045 return msg
2030 2046
2031 2047 def develwarn(self, msg, stacklevel=1, config=None):
2032 2048 """issue a developer warning message
2033 2049
2034 2050 Use 'stacklevel' to report the offender some layers further up in the
2035 2051 stack.
2036 2052 """
2037 2053 if not self.configbool(b'devel', b'all-warnings'):
2038 2054 if config is None or not self.configbool(b'devel', config):
2039 2055 return
2040 2056 msg = b'devel-warn: ' + msg
2041 2057 stacklevel += 1 # get in develwarn
2042 2058 if self.tracebackflag:
2043 2059 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
2044 2060 self.log(
2045 2061 b'develwarn',
2046 2062 b'%s at:\n%s'
2047 2063 % (msg, b''.join(util.getstackframes(stacklevel))),
2048 2064 )
2049 2065 else:
2050 2066 curframe = inspect.currentframe()
2051 2067 calframe = inspect.getouterframes(curframe, 2)
2052 2068 fname, lineno, fmsg = calframe[stacklevel][1:4]
2053 2069 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2054 2070 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2055 2071 self.log(
2056 2072 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2057 2073 )
2058 2074
2059 2075 # avoid cycles
2060 2076 del curframe
2061 2077 del calframe
2062 2078
2063 2079 def deprecwarn(self, msg, version, stacklevel=2):
2064 2080 """issue a deprecation warning
2065 2081
2066 2082 - msg: message explaining what is deprecated and how to upgrade,
2067 2083 - version: last version where the API will be supported,
2068 2084 """
2069 2085 if not (
2070 2086 self.configbool(b'devel', b'all-warnings')
2071 2087 or self.configbool(b'devel', b'deprec-warn')
2072 2088 ):
2073 2089 return
2074 2090 msg += (
2075 2091 b"\n(compatibility will be dropped after Mercurial-%s,"
2076 2092 b" update your code.)"
2077 2093 ) % version
2078 2094 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2079 2095
2080 2096 def exportableenviron(self):
2081 2097 """The environment variables that are safe to export, e.g. through
2082 2098 hgweb.
2083 2099 """
2084 2100 return self._exportableenviron
2085 2101
2086 2102 @contextlib.contextmanager
2087 2103 def configoverride(self, overrides, source=b""):
2088 2104 """Context manager for temporary config overrides
2089 2105 `overrides` must be a dict of the following structure:
2090 2106 {(section, name) : value}"""
2091 2107 backups = {}
2092 2108 try:
2093 2109 for (section, name), value in overrides.items():
2094 2110 backups[(section, name)] = self.backupconfig(section, name)
2095 2111 self.setconfig(section, name, value, source)
2096 2112 yield
2097 2113 finally:
2098 2114 for __, backup in backups.items():
2099 2115 self.restoreconfig(backup)
2100 2116 # just restoring ui.quiet config to the previous value is not enough
2101 2117 # as it does not update ui.quiet class member
2102 2118 if (b'ui', b'quiet') in overrides:
2103 2119 self.fixconfig(section=b'ui')
2104 2120
2105 2121
2106 2122 class paths(dict):
2107 2123 """Represents a collection of paths and their configs.
2108 2124
2109 2125 Data is initially derived from ui instances and the config files they have
2110 2126 loaded.
2111 2127 """
2112 2128
2113 2129 def __init__(self, ui):
2114 2130 dict.__init__(self)
2115 2131
2116 2132 for name, loc in ui.configitems(b'paths', ignoresub=True):
2117 2133 # No location is the same as not existing.
2118 2134 if not loc:
2119 2135 continue
2120 2136 loc, sub = ui.configsuboptions(b'paths', name)
2121 2137 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
2122 2138
2123 2139 def getpath(self, name, default=None):
2124 2140 """Return a ``path`` from a string, falling back to default.
2125 2141
2126 2142 ``name`` can be a named path or locations. Locations are filesystem
2127 2143 paths or URIs.
2128 2144
2129 2145 Returns None if ``name`` is not a registered path, a URI, or a local
2130 2146 path to a repo.
2131 2147 """
2132 2148 # Only fall back to default if no path was requested.
2133 2149 if name is None:
2134 2150 if not default:
2135 2151 default = ()
2136 2152 elif not isinstance(default, (tuple, list)):
2137 2153 default = (default,)
2138 2154 for k in default:
2139 2155 try:
2140 2156 return self[k]
2141 2157 except KeyError:
2142 2158 continue
2143 2159 return None
2144 2160
2145 2161 # Most likely empty string.
2146 2162 # This may need to raise in the future.
2147 2163 if not name:
2148 2164 return None
2149 2165
2150 2166 try:
2151 2167 return self[name]
2152 2168 except KeyError:
2153 2169 # Try to resolve as a local path or URI.
2154 2170 try:
2155 2171 # We don't pass sub-options in, so no need to pass ui instance.
2156 2172 return path(None, None, rawloc=name)
2157 2173 except ValueError:
2158 2174 raise error.RepoError(_(b'repository %s does not exist') % name)
2159 2175
2160 2176
2161 2177 _pathsuboptions = {}
2162 2178
2163 2179
2164 2180 def pathsuboption(option, attr):
2165 2181 """Decorator used to declare a path sub-option.
2166 2182
2167 2183 Arguments are the sub-option name and the attribute it should set on
2168 2184 ``path`` instances.
2169 2185
2170 2186 The decorated function will receive as arguments a ``ui`` instance,
2171 2187 ``path`` instance, and the string value of this option from the config.
2172 2188 The function should return the value that will be set on the ``path``
2173 2189 instance.
2174 2190
2175 2191 This decorator can be used to perform additional verification of
2176 2192 sub-options and to change the type of sub-options.
2177 2193 """
2178 2194
2179 2195 def register(func):
2180 2196 _pathsuboptions[option] = (attr, func)
2181 2197 return func
2182 2198
2183 2199 return register
2184 2200
2185 2201
2186 2202 @pathsuboption(b'pushurl', b'pushloc')
2187 2203 def pushurlpathoption(ui, path, value):
2188 2204 u = util.url(value)
2189 2205 # Actually require a URL.
2190 2206 if not u.scheme:
2191 2207 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
2192 2208 return None
2193 2209
2194 2210 # Don't support the #foo syntax in the push URL to declare branch to
2195 2211 # push.
2196 2212 if u.fragment:
2197 2213 ui.warn(
2198 2214 _(
2199 2215 b'("#fragment" in paths.%s:pushurl not supported; '
2200 2216 b'ignoring)\n'
2201 2217 )
2202 2218 % path.name
2203 2219 )
2204 2220 u.fragment = None
2205 2221
2206 2222 return bytes(u)
2207 2223
2208 2224
2209 2225 @pathsuboption(b'pushrev', b'pushrev')
2210 2226 def pushrevpathoption(ui, path, value):
2211 2227 return value
2212 2228
2213 2229
2214 2230 class path(object):
2215 2231 """Represents an individual path and its configuration."""
2216 2232
2217 2233 def __init__(self, ui, name, rawloc=None, suboptions=None):
2218 2234 """Construct a path from its config options.
2219 2235
2220 2236 ``ui`` is the ``ui`` instance the path is coming from.
2221 2237 ``name`` is the symbolic name of the path.
2222 2238 ``rawloc`` is the raw location, as defined in the config.
2223 2239 ``pushloc`` is the raw locations pushes should be made to.
2224 2240
2225 2241 If ``name`` is not defined, we require that the location be a) a local
2226 2242 filesystem path with a .hg directory or b) a URL. If not,
2227 2243 ``ValueError`` is raised.
2228 2244 """
2229 2245 if not rawloc:
2230 2246 raise ValueError(b'rawloc must be defined')
2231 2247
2232 2248 # Locations may define branches via syntax <base>#<branch>.
2233 2249 u = util.url(rawloc)
2234 2250 branch = None
2235 2251 if u.fragment:
2236 2252 branch = u.fragment
2237 2253 u.fragment = None
2238 2254
2239 2255 self.url = u
2240 2256 self.branch = branch
2241 2257
2242 2258 self.name = name
2243 2259 self.rawloc = rawloc
2244 2260 self.loc = b'%s' % u
2245 2261
2246 2262 # When given a raw location but not a symbolic name, validate the
2247 2263 # location is valid.
2248 2264 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
2249 2265 raise ValueError(
2250 2266 b'location is not a URL or path to a local '
2251 2267 b'repo: %s' % rawloc
2252 2268 )
2253 2269
2254 2270 suboptions = suboptions or {}
2255 2271
2256 2272 # Now process the sub-options. If a sub-option is registered, its
2257 2273 # attribute will always be present. The value will be None if there
2258 2274 # was no valid sub-option.
2259 2275 for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions):
2260 2276 if suboption not in suboptions:
2261 2277 setattr(self, attr, None)
2262 2278 continue
2263 2279
2264 2280 value = func(ui, self, suboptions[suboption])
2265 2281 setattr(self, attr, value)
2266 2282
2267 2283 def _isvalidlocalpath(self, path):
2268 2284 """Returns True if the given path is a potentially valid repository.
2269 2285 This is its own function so that extensions can change the definition of
2270 2286 'valid' in this case (like when pulling from a git repo into a hg
2271 2287 one)."""
2272 2288 try:
2273 2289 return os.path.isdir(os.path.join(path, b'.hg'))
2274 2290 # Python 2 may return TypeError. Python 3, ValueError.
2275 2291 except (TypeError, ValueError):
2276 2292 return False
2277 2293
2278 2294 @property
2279 2295 def suboptions(self):
2280 2296 """Return sub-options and their values for this path.
2281 2297
2282 2298 This is intended to be used for presentation purposes.
2283 2299 """
2284 2300 d = {}
2285 2301 for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions):
2286 2302 value = getattr(self, attr)
2287 2303 if value is not None:
2288 2304 d[subopt] = value
2289 2305 return d
2290 2306
2291 2307
2292 2308 # we instantiate one globally shared progress bar to avoid
2293 2309 # competing progress bars when multiple UI objects get created
2294 2310 _progresssingleton = None
2295 2311
2296 2312
2297 2313 def getprogbar(ui):
2298 2314 global _progresssingleton
2299 2315 if _progresssingleton is None:
2300 2316 # passing 'ui' object to the singleton is fishy,
2301 2317 # this is how the extension used to work but feel free to rework it.
2302 2318 _progresssingleton = progress.progbar(ui)
2303 2319 return _progresssingleton
2304 2320
2305 2321
2306 2322 def haveprogbar():
2307 2323 return _progresssingleton is not None
2308 2324
2309 2325
2310 2326 def _selectmsgdests(ui):
2311 2327 name = ui.config(b'ui', b'message-output')
2312 2328 if name == b'channel':
2313 2329 if ui.fmsg:
2314 2330 return ui.fmsg, ui.fmsg
2315 2331 else:
2316 2332 # fall back to ferr if channel isn't ready so that status/error
2317 2333 # messages can be printed
2318 2334 return ui.ferr, ui.ferr
2319 2335 if name == b'stdio':
2320 2336 return ui.fout, ui.ferr
2321 2337 if name == b'stderr':
2322 2338 return ui.ferr, ui.ferr
2323 2339 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
2324 2340
2325 2341
2326 2342 def _writemsgwith(write, dest, *args, **opts):
2327 2343 """Write ui message with the given ui._write*() function
2328 2344
2329 2345 The specified message type is translated to 'ui.<type>' label if the dest
2330 2346 isn't a structured channel, so that the message will be colorized.
2331 2347 """
2332 2348 # TODO: maybe change 'type' to a mandatory option
2333 2349 if 'type' in opts and not getattr(dest, 'structured', False):
2334 2350 opts['label'] = opts.get('label', b'') + b' ui.%s' % opts.pop('type')
2335 2351 write(dest, *args, **opts)
@@ -1,363 +1,403 b''
1 1
2 2 $ cat > loop.py <<EOF
3 3 > from __future__ import absolute_import
4 4 > import time
5 5 > from mercurial import commands, registrar
6 6 >
7 7 > cmdtable = {}
8 8 > command = registrar.command(cmdtable)
9 9 >
10 10 > class incrementingtime(object):
11 11 > def __init__(self):
12 12 > self._time = 0.0
13 13 > def __call__(self):
14 14 > self._time += 0.25
15 15 > return self._time
16 16 > time.time = incrementingtime()
17 17 >
18 18 > @command(b'loop',
19 19 > [(b'', b'total', b'', b'override for total'),
20 20 > (b'', b'nested', False, b'show nested results'),
21 > (b'', b'parallel', False, b'show parallel sets of results')],
21 > (b'', b'parallel', False, b'show parallel sets of results'),
22 > (b'', b'warn', False, b'show warning if step divisible by 3')],
22 23 > b'hg loop LOOPS',
23 24 > norepo=True)
24 25 > def loop(ui, loops, **opts):
25 26 > loops = int(loops)
26 27 > total = None
27 28 > if loops >= 0:
28 29 > total = loops
29 30 > if opts.get('total', None):
30 31 > total = int(opts.get('total'))
31 32 > nested = False
32 33 > if opts.get('nested', None):
33 34 > nested = True
34 35 > loops = abs(loops)
36 > showwarn = opts.get('warn', False)
35 37 >
36 38 > progress = ui.makeprogress(topiclabel, unit=b'loopnum', total=total)
37 39 > other = ui.makeprogress(b'other', unit=b'othernum', total=total)
38 40 > for i in range(loops):
39 41 > progress.update(i, item=getloopitem(i))
40 42 > if opts.get('parallel'):
41 43 > other.update(i, item=b'other.%d' % i)
42 44 > if nested:
43 45 > nested_steps = 2
44 46 > if i and i % 4 == 0:
45 47 > nested_steps = 5
46 48 > nested = ui.makeprogress(b'nested', unit=b'nestnum',
47 49 > total=nested_steps)
48 50 > for j in range(nested_steps):
49 51 > nested.update(j, item=b'nested.%d' % j)
50 52 > nested.complete()
53 > if showwarn and i % 3 == 0:
54 > ui.warn(b'reached step %d\n' %i)
51 55 > progress.complete()
52 56 >
53 57 > topiclabel = b'loop'
54 58 > def getloopitem(i):
55 59 > return b'loop.%d' % i
56 60 >
57 61 > EOF
58 62
59 63 $ cp $HGRCPATH $HGRCPATH.orig
60 64 $ echo "[extensions]" >> $HGRCPATH
61 65 $ echo "progress=" >> $HGRCPATH
62 66 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
63 67 $ echo "[progress]" >> $HGRCPATH
64 68 $ echo "format = topic bar number" >> $HGRCPATH
65 69 $ echo "assume-tty=1" >> $HGRCPATH
66 70 $ echo "width=60" >> $HGRCPATH
67 71
68 72 test default params, display nothing because of delay
69 73
70 74 $ hg -y loop 3
71 75 $ echo "delay=0" >> $HGRCPATH
72 76 $ echo "refresh=0" >> $HGRCPATH
73 77
74 78 test with delay=0, refresh=0
75 79
76 80 $ hg -y loop 3
77 81 \r (no-eol) (esc)
78 82 loop [ ] 0/3\r (no-eol) (esc)
79 83 loop [===============> ] 1/3\r (no-eol) (esc)
80 84 loop [===============================> ] 2/3\r (no-eol) (esc)
81 85 \r (no-eol) (esc)
82 86 no progress with --quiet
83 87 $ hg -y loop 3 --quiet
84 88
85 89 test plain mode exception
86 90 $ HGPLAINEXCEPT=progress hg -y loop 1
87 91 \r (no-eol) (esc)
88 92 loop [ ] 0/1\r (no-eol) (esc)
89 93 \r (no-eol) (esc)
90 94
91 95 test nested short-lived topics (which shouldn't display with nestdelay):
92 96
93 97 $ hg -y loop 3 --nested
94 98 \r (no-eol) (esc)
95 99 loop [ ] 0/3\r (no-eol) (esc)
96 100 loop [===============> ] 1/3\r (no-eol) (esc)
97 101 loop [===============================> ] 2/3\r (no-eol) (esc)
98 102 \r (no-eol) (esc)
99 103
100 104 Test nested long-lived topic which has the same name as a short-lived
101 105 peer. We shouldn't get stuck showing the short-lived inner steps, and
102 106 should go back to skipping the inner steps when the slow nested step
103 107 finishes.
104 108
105 109 $ hg -y loop 7 --nested
106 110 \r (no-eol) (esc)
107 111 loop [ ] 0/7\r (no-eol) (esc)
108 112 loop [=====> ] 1/7\r (no-eol) (esc)
109 113 loop [============> ] 2/7\r (no-eol) (esc)
110 114 loop [===================> ] 3/7\r (no-eol) (esc)
111 115 loop [==========================> ] 4/7\r (no-eol) (esc)
112 116 nested [==========================> ] 3/5\r (no-eol) (esc)
113 117 nested [===================================> ] 4/5\r (no-eol) (esc)
114 118 loop [=================================> ] 5/7\r (no-eol) (esc)
115 119 loop [========================================> ] 6/7\r (no-eol) (esc)
116 120 \r (no-eol) (esc)
117 121
118 122
119 123 $ hg --config progress.changedelay=0 -y loop 3 --nested
120 124 \r (no-eol) (esc)
121 125 loop [ ] 0/3\r (no-eol) (esc)
122 126 nested [ ] 0/2\r (no-eol) (esc)
123 127 nested [======================> ] 1/2\r (no-eol) (esc)
124 128 loop [===============> ] 1/3\r (no-eol) (esc)
125 129 nested [ ] 0/2\r (no-eol) (esc)
126 130 nested [======================> ] 1/2\r (no-eol) (esc)
127 131 loop [===============================> ] 2/3\r (no-eol) (esc)
128 132 nested [ ] 0/2\r (no-eol) (esc)
129 133 nested [======================> ] 1/2\r (no-eol) (esc)
130 134 \r (no-eol) (esc)
131 135
132 136
133 137 test two topics being printed in parallel (as when we're doing a local
134 138 --pull clone, where you get the unbundle and bundle progress at the
135 139 same time):
136 140 $ hg loop 3 --parallel
137 141 \r (no-eol) (esc)
138 142 loop [ ] 0/3\r (no-eol) (esc)
139 143 loop [===============> ] 1/3\r (no-eol) (esc)
140 144 loop [===============================> ] 2/3\r (no-eol) (esc)
141 145 \r (no-eol) (esc)
142 146 test refresh is taken in account
143 147
144 148 $ hg -y --config progress.refresh=100 loop 3
145 149
146 150 test format options 1
147 151
148 152 $ hg -y --config 'progress.format=number topic item+2' loop 2
149 153 \r (no-eol) (esc)
150 154 0/2 loop lo\r (no-eol) (esc)
151 155 1/2 loop lo\r (no-eol) (esc)
152 156 \r (no-eol) (esc)
153 157
154 158 test format options 2
155 159
156 160 $ hg -y --config 'progress.format=number item-3 bar' loop 2
157 161 \r (no-eol) (esc)
158 162 0/2 p.0 [ ]\r (no-eol) (esc)
159 163 1/2 p.1 [=======================> ]\r (no-eol) (esc)
160 164 \r (no-eol) (esc)
161 165
162 166 test format options and indeterminate progress
163 167
164 168 $ hg -y --config 'progress.format=number item bar' loop -- -2
165 169 \r (no-eol) (esc)
166 170 0 loop.0 [ <=> ]\r (no-eol) (esc)
167 171 1 loop.1 [ <=> ]\r (no-eol) (esc)
168 172 \r (no-eol) (esc)
169 173
170 174 make sure things don't fall over if count > total
171 175
172 176 $ hg -y loop --total 4 6
173 177 \r (no-eol) (esc)
174 178 loop [ ] 0/4\r (no-eol) (esc)
175 179 loop [===========> ] 1/4\r (no-eol) (esc)
176 180 loop [=======================> ] 2/4\r (no-eol) (esc)
177 181 loop [===================================> ] 3/4\r (no-eol) (esc)
178 182 loop [===============================================>] 4/4\r (no-eol) (esc)
179 183 loop [ <=> ] 5/4\r (no-eol) (esc)
180 184 \r (no-eol) (esc)
181 185
186 test interaction with ui.warn
187
188 $ hg loop --warn 6
189 \r (no-eol) (esc)
190 loop [ ] 0/6\r (no-eol) (esc)
191 \r (no-eol) (esc)
192 reached step 0
193 \r (no-eol) (esc)
194 loop [=======> ] 1/6\r (no-eol) (esc)
195 loop [===============> ] 2/6\r (no-eol) (esc)
196 loop [=======================> ] 3/6\r (no-eol) (esc)
197 \r (no-eol) (esc)
198 reached step 3
199 \r (no-eol) (esc)
200 loop [===============================> ] 4/6\r (no-eol) (esc)
201 loop [=======================================> ] 5/6\r (no-eol) (esc)
202 \r (no-eol) (esc)
203
204 test interaction with ui.timestamp-output
205
206 $ hg loop --warn --config ui.timestamp-output=true 6
207 \r (no-eol) (esc)
208 loop [ ] 0/6\r (no-eol) (esc)
209 \r (no-eol) (esc)
210 \[20[2-9][0-9]-[01][0-9]-[0-3][0-9]T[0-5][0-9]:[0-5][0-9]:[0-5][0-9]\.[0-9][0-9][0-9][0-9][0-9][0-9]\] reached step 0 (re)
211 \r (no-eol) (esc)
212 loop [=======> ] 1/6\r (no-eol) (esc)
213 loop [===============> ] 2/6\r (no-eol) (esc)
214 loop [=======================> ] 3/6\r (no-eol) (esc)
215 \r (no-eol) (esc)
216 \[20[2-9][0-9]-[01][0-9]-[0-3][0-9]T[0-5][0-9]:[0-5][0-9]:[0-5][0-9]\.[0-9][0-9][0-9][0-9][0-9][0-9]\] reached step 3 (re)
217 \r (no-eol) (esc)
218 loop [===============================> ] 4/6\r (no-eol) (esc)
219 loop [=======================================> ] 5/6\r (no-eol) (esc)
220 \r (no-eol) (esc)
221
182 222 test immediate progress completion
183 223
184 224 $ hg -y loop 0
185 225
186 226 test delay time estimates
187 227
188 228 #if no-chg
189 229
190 230 $ cp $HGRCPATH.orig $HGRCPATH
191 231 $ echo "[extensions]" >> $HGRCPATH
192 232 $ echo "mocktime=$TESTDIR/mocktime.py" >> $HGRCPATH
193 233 $ echo "progress=" >> $HGRCPATH
194 234 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
195 235 $ echo "[progress]" >> $HGRCPATH
196 236 $ echo "assume-tty=1" >> $HGRCPATH
197 237 $ echo "delay=25" >> $HGRCPATH
198 238 $ echo "width=60" >> $HGRCPATH
199 239
200 240 $ MOCKTIME=11 hg -y loop 8
201 241 \r (no-eol) (esc)
202 242 loop [=========> ] 2/8 1m07s\r (no-eol) (esc)
203 243 loop [===============> ] 3/8 56s\r (no-eol) (esc)
204 244 loop [=====================> ] 4/8 45s\r (no-eol) (esc)
205 245 loop [==========================> ] 5/8 34s\r (no-eol) (esc)
206 246 loop [================================> ] 6/8 23s\r (no-eol) (esc)
207 247 loop [=====================================> ] 7/8 12s\r (no-eol) (esc)
208 248 \r (no-eol) (esc)
209 249
210 250 $ MOCKTIME=10000 hg -y loop 4
211 251 \r (no-eol) (esc)
212 252 loop [ ] 0/4\r (no-eol) (esc)
213 253 loop [=========> ] 1/4 8h21m\r (no-eol) (esc)
214 254 loop [====================> ] 2/4 5h34m\r (no-eol) (esc)
215 255 loop [==============================> ] 3/4 2h47m\r (no-eol) (esc)
216 256 \r (no-eol) (esc)
217 257
218 258 $ MOCKTIME=1000000 hg -y loop 4
219 259 \r (no-eol) (esc)
220 260 loop [ ] 0/4\r (no-eol) (esc)
221 261 loop [=========> ] 1/4 5w00d\r (no-eol) (esc)
222 262 loop [====================> ] 2/4 3w03d\r (no-eol) (esc)
223 263 loop [=============================> ] 3/4 11d14h\r (no-eol) (esc)
224 264 \r (no-eol) (esc)
225 265
226 266
227 267 $ MOCKTIME=14000000 hg -y loop 4
228 268 \r (no-eol) (esc)
229 269 loop [ ] 0/4\r (no-eol) (esc)
230 270 loop [=========> ] 1/4 1y18w\r (no-eol) (esc)
231 271 loop [===================> ] 2/4 46w03d\r (no-eol) (esc)
232 272 loop [=============================> ] 3/4 23w02d\r (no-eol) (esc)
233 273 \r (no-eol) (esc)
234 274
235 275 Non-linear progress:
236 276
237 277 $ MOCKTIME='20 20 20 20 20 20 20 20 20 20 500 500 500 500 500 20 20 20 20 20' hg -y loop 20
238 278 \r (no-eol) (esc)
239 279 loop [=> ] 1/20 6m21s\r (no-eol) (esc)
240 280 loop [===> ] 2/20 6m01s\r (no-eol) (esc)
241 281 loop [=====> ] 3/20 5m41s\r (no-eol) (esc)
242 282 loop [=======> ] 4/20 5m21s\r (no-eol) (esc)
243 283 loop [=========> ] 5/20 5m01s\r (no-eol) (esc)
244 284 loop [===========> ] 6/20 4m41s\r (no-eol) (esc)
245 285 loop [=============> ] 7/20 4m21s\r (no-eol) (esc)
246 286 loop [===============> ] 8/20 4m01s\r (no-eol) (esc)
247 287 loop [================> ] 9/20 25m40s\r (no-eol) (esc)
248 288 loop [===================> ] 10/20 1h06m\r (no-eol) (esc)
249 289 loop [=====================> ] 11/20 1h13m\r (no-eol) (esc)
250 290 loop [=======================> ] 12/20 1h07m\r (no-eol) (esc)
251 291 loop [========================> ] 13/20 58m19s\r (no-eol) (esc)
252 292 loop [===========================> ] 14/20 7m09s\r (no-eol) (esc)
253 293 loop [=============================> ] 15/20 3m38s\r (no-eol) (esc)
254 294 loop [===============================> ] 16/20 2m15s\r (no-eol) (esc)
255 295 loop [=================================> ] 17/20 1m27s\r (no-eol) (esc)
256 296 loop [====================================> ] 18/20 52s\r (no-eol) (esc)
257 297 loop [======================================> ] 19/20 25s\r (no-eol) (esc)
258 298 \r (no-eol) (esc)
259 299
260 300 Time estimates should not fail when there's no end point:
261 301 $ MOCKTIME=11 hg -y loop -- -4
262 302 \r (no-eol) (esc)
263 303 loop [ <=> ] 2\r (no-eol) (esc)
264 304 loop [ <=> ] 3\r (no-eol) (esc)
265 305 \r (no-eol) (esc)
266 306
267 307 #endif
268 308
269 309 test line trimming by '[progress] width', when progress topic contains
270 310 multi-byte characters, of which length of byte sequence and columns in
271 311 display are different from each other.
272 312
273 313 $ cp $HGRCPATH.orig $HGRCPATH
274 314 $ cat >> $HGRCPATH <<EOF
275 315 > [extensions]
276 316 > progress=
277 317 > loop=`pwd`/loop.py
278 318 > [progress]
279 319 > assume-tty = 1
280 320 > delay = 0
281 321 > refresh = 0
282 322 > EOF
283 323
284 324 $ rm -f loop.pyc
285 325 $ cat >> loop.py <<EOF
286 326 > # use non-ascii characters as topic label of progress
287 327 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
288 328 > topiclabel = u'\u3042\u3044\u3046\u3048'.encode('utf-8')
289 329 > EOF
290 330
291 331 $ cat >> $HGRCPATH <<EOF
292 332 > [progress]
293 333 > format = topic number
294 334 > width= 12
295 335 > EOF
296 336
297 337 $ hg --encoding utf-8 -y loop --total 3 3
298 338 \r (no-eol) (esc)
299 339 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88 0/3\r (no-eol) (esc)
300 340 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88 1/3\r (no-eol) (esc)
301 341 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88 2/3\r (no-eol) (esc)
302 342 \r (no-eol) (esc)
303 343
304 344 test calculation of bar width, when progress topic contains multi-byte
305 345 characters, of which length of byte sequence and columns in display
306 346 are different from each other.
307 347
308 348 $ cat >> $HGRCPATH <<EOF
309 349 > [progress]
310 350 > format = topic bar
311 351 > width= 21
312 352 > # progwidth should be 9 (= 21 - (8+1) - 3)
313 353 > EOF
314 354
315 355 $ hg --encoding utf-8 -y loop --total 3 3
316 356 \r (no-eol) (esc)
317 357 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88 [ ]\r (no-eol) (esc)
318 358 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88 [==> ]\r (no-eol) (esc)
319 359 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88 [=====> ]\r (no-eol) (esc)
320 360 \r (no-eol) (esc)
321 361
322 362 test trimming progress items, when they contain multi-byte characters,
323 363 of which length of byte sequence and columns in display are different
324 364 from each other.
325 365
326 366 $ rm -f loop.pyc
327 367 $ rm -Rf __pycache__
328 368 $ cat >> loop.py <<EOF
329 369 > # use non-ascii characters as loop items of progress
330 370 > loopitems = [
331 371 > u'\u3042\u3044'.encode('utf-8'), # 2 x 2 = 4 columns
332 372 > u'\u3042\u3044\u3046'.encode('utf-8'), # 2 x 3 = 6 columns
333 373 > u'\u3042\u3044\u3046\u3048'.encode('utf-8'), # 2 x 4 = 8 columns
334 374 > ]
335 375 > def getloopitem(i):
336 376 > return loopitems[i % len(loopitems)]
337 377 > EOF
338 378
339 379 $ cat >> $HGRCPATH <<EOF
340 380 > [progress]
341 381 > # trim at tail side
342 382 > format = item+6
343 383 > EOF
344 384
345 385 $ hg --encoding utf-8 -y loop --total 3 3
346 386 \r (no-eol) (esc)
347 387 \xe3\x81\x82\xe3\x81\x84 \r (no-eol) (esc)
348 388 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\r (no-eol) (esc)
349 389 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\r (no-eol) (esc)
350 390 \r (no-eol) (esc)
351 391
352 392 $ cat >> $HGRCPATH <<EOF
353 393 > [progress]
354 394 > # trim at left side
355 395 > format = item-6
356 396 > EOF
357 397
358 398 $ hg --encoding utf-8 -y loop --total 3 3
359 399 \r (no-eol) (esc)
360 400 \xe3\x81\x82\xe3\x81\x84 \r (no-eol) (esc)
361 401 \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\r (no-eol) (esc)
362 402 \xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\r (no-eol) (esc)
363 403 \r (no-eol) (esc)
@@ -1,152 +1,152 b''
1 1 #require serve
2 2
3 3 #testcases sshv1 sshv2
4 4
5 5 #if sshv2
6 6 $ cat >> $HGRCPATH << EOF
7 7 > [experimental]
8 8 > sshpeer.advertise-v2 = true
9 9 > sshserver.support-v2 = true
10 10 > EOF
11 11 #endif
12 12
13 13 $ hg init test
14 14 $ cd test
15 15
16 16 $ echo foo>foo
17 17 $ hg addremove
18 18 adding foo
19 19 $ hg commit -m 1
20 20
21 21 $ hg verify
22 22 checking changesets
23 23 checking manifests
24 24 crosschecking files in changesets and manifests
25 25 checking files
26 26 checked 1 changesets with 1 changes to 1 files
27 27
28 28 $ hg serve -p $HGPORT -d --pid-file=hg.pid
29 29 $ cat hg.pid >> $DAEMON_PIDS
30 30 $ cd ..
31 31
32 32 $ hg clone --pull http://foo:bar@localhost:$HGPORT/ copy
33 33 requesting all changes
34 34 adding changesets
35 35 adding manifests
36 36 adding file changes
37 37 added 1 changesets with 1 changes to 1 files
38 38 new changesets 340e38bdcde4
39 39 updating to branch default
40 40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 41
42 42 $ cd copy
43 43 $ hg verify
44 44 checking changesets
45 45 checking manifests
46 46 crosschecking files in changesets and manifests
47 47 checking files
48 48 checked 1 changesets with 1 changes to 1 files
49 49
50 50 $ hg co
51 51 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 52 $ cat foo
53 53 foo
54 54
55 55 $ hg manifest --debug
56 56 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
57 57
58 58 $ hg pull
59 59 pulling from http://foo@localhost:$HGPORT/
60 60 searching for changes
61 61 no changes found
62 62
63 63 $ hg rollback --dry-run --verbose
64 64 repository tip rolled back to revision -1 (undo pull: http://foo:***@localhost:$HGPORT/)
65 65
66 66 Test pull of non-existing 20 character revision specification, making sure plain ascii identifiers
67 67 not are encoded like a node:
68 68
69 69 $ hg pull -r 'xxxxxxxxxxxxxxxxxxxy'
70 70 pulling from http://foo@localhost:$HGPORT/
71 71 abort: unknown revision 'xxxxxxxxxxxxxxxxxxxy'!
72 72 [255]
73 73 $ hg pull -r 'xxxxxxxxxxxxxxxxxx y'
74 74 pulling from http://foo@localhost:$HGPORT/
75 75 abort: unknown revision 'xxxxxxxxxxxxxxxxxx y'!
76 76 [255]
77 77
78 78 Test pull of working copy revision
79 79 $ hg pull -r 'ffffffffffff'
80 80 pulling from http://foo@localhost:$HGPORT/
81 81 abort: unknown revision 'ffffffffffff'!
82 82 [255]
83 83
84 84 Issue622: hg init && hg pull -u URL doesn't checkout default branch
85 85
86 86 $ cd ..
87 87 $ hg init empty
88 88 $ cd empty
89 89 $ hg pull -u ../test
90 90 pulling from ../test
91 91 requesting all changes
92 92 adding changesets
93 93 adding manifests
94 94 adding file changes
95 95 added 1 changesets with 1 changes to 1 files
96 96 new changesets 340e38bdcde4
97 97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98
99 99 Test 'file:' uri handling:
100 100
101 101 $ hg pull -q file://../test-does-not-exist
102 102 abort: file:// URLs can only refer to localhost
103 103 [255]
104 104
105 105 $ hg pull -q file://../test
106 106 abort: file:// URLs can only refer to localhost
107 107 [255]
108 108
109 109 MSYS changes 'file:' into 'file;'
110 110
111 111 #if no-msys
112 112 $ hg pull -q file:../test # no-msys
113 113 #endif
114 114
115 115 It's tricky to make file:// URLs working on every platform with
116 116 regular shell commands.
117 117
118 118 $ URL=`"$PYTHON" -c "from __future__ import print_function; import os; print('file://foobar' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test')"`
119 119 $ hg pull -q "$URL"
120 120 abort: file:// URLs can only refer to localhost
121 121 [255]
122 122
123 123 $ URL=`"$PYTHON" -c "from __future__ import print_function; import os; print('file://localhost' + ('/' + os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test')"`
124 124 $ hg pull -q "$URL"
125 125
126 126 SEC: check for unsafe ssh url
127 127
128 128 $ cat >> $HGRCPATH << EOF
129 129 > [ui]
130 130 > ssh = sh -c "read l; read l; read l"
131 131 > EOF
132 132
133 133 $ hg pull 'ssh://-oProxyCommand=touch${IFS}owned/path'
134 134 pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
135 135 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
136 136 [255]
137 137 $ hg pull 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
138 138 pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
139 139 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
140 140 [255]
141 141 $ hg pull 'ssh://fakehost|touch${IFS}owned/path'
142 142 pulling from ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
143 143 abort: no suitable response from remote hg!
144 144 [255]
145 $ hg pull 'ssh://fakehost%7Ctouch%20owned/path'
146 pulling from ssh://fakehost%7Ctouch%20owned/path
147 abort: no suitable response from remote hg!
145 $ hg --config ui.timestamp-output=true pull 'ssh://fakehost%7Ctouch%20owned/path'
146 \[20[2-9][0-9]-[01][0-9]-[0-3][0-9]T[0-5][0-9]:[0-5][0-9]:[0-5][0-9]\.[0-9][0-9][0-9][0-9][0-9][0-9]\] pulling from ssh://fakehost%7Ctouch%20owned/path (re)
147 \[20[2-9][0-9]-[01][0-9]-[0-3][0-9]T[0-5][0-9]:[0-5][0-9]:[0-5][0-9]\.[0-9][0-9][0-9][0-9][0-9][0-9]\] abort: no suitable response from remote hg! (re)
148 148 [255]
149 149
150 150 $ [ ! -f owned ] || echo 'you got owned'
151 151
152 152 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now