##// END OF EJS Templates
log: add config for making `hg log -G` always topo-sorted...
Martin von Zweigbergk -
r42496:a39f8aa6 default draft
parent child Browse files
Show More
@@ -1,1487 +1,1490
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import functools
11 11 import re
12 12
13 13 from . import (
14 14 encoding,
15 15 error,
16 16 )
17 17
18 18 def loadconfigtable(ui, extname, configtable):
19 19 """update config item known to the ui with the extension ones"""
20 20 for section, items in sorted(configtable.items()):
21 21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 22 knownkeys = set(knownitems)
23 23 newkeys = set(items)
24 24 for key in sorted(knownkeys & newkeys):
25 25 msg = "extension '%s' overwrite config item '%s.%s'"
26 26 msg %= (extname, section, key)
27 27 ui.develwarn(msg, config='warn-config')
28 28
29 29 knownitems.update(items)
30 30
31 31 class configitem(object):
32 32 """represent a known config item
33 33
34 34 :section: the official config section where to find this item,
35 35 :name: the official name within the section,
36 36 :default: default value for this item,
37 37 :alias: optional list of tuples as alternatives,
38 38 :generic: this is a generic definition, match name using regular expression.
39 39 """
40 40
41 41 def __init__(self, section, name, default=None, alias=(),
42 42 generic=False, priority=0):
43 43 self.section = section
44 44 self.name = name
45 45 self.default = default
46 46 self.alias = list(alias)
47 47 self.generic = generic
48 48 self.priority = priority
49 49 self._re = None
50 50 if generic:
51 51 self._re = re.compile(self.name)
52 52
53 53 class itemregister(dict):
54 54 """A specialized dictionary that can handle wild-card selection"""
55 55
56 56 def __init__(self):
57 57 super(itemregister, self).__init__()
58 58 self._generics = set()
59 59
60 60 def update(self, other):
61 61 super(itemregister, self).update(other)
62 62 self._generics.update(other._generics)
63 63
64 64 def __setitem__(self, key, item):
65 65 super(itemregister, self).__setitem__(key, item)
66 66 if item.generic:
67 67 self._generics.add(item)
68 68
69 69 def get(self, key):
70 70 baseitem = super(itemregister, self).get(key)
71 71 if baseitem is not None and not baseitem.generic:
72 72 return baseitem
73 73
74 74 # search for a matching generic item
75 75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
76 76 for item in generics:
77 77 # we use 'match' instead of 'search' to make the matching simpler
78 78 # for people unfamiliar with regular expression. Having the match
79 79 # rooted to the start of the string will produce less surprising
80 80 # result for user writing simple regex for sub-attribute.
81 81 #
82 82 # For example using "color\..*" match produces an unsurprising
83 83 # result, while using search could suddenly match apparently
84 84 # unrelated configuration that happens to contains "color."
85 85 # anywhere. This is a tradeoff where we favor requiring ".*" on
86 86 # some match to avoid the need to prefix most pattern with "^".
87 87 # The "^" seems more error prone.
88 88 if item._re.match(key):
89 89 return item
90 90
91 91 return None
92 92
93 93 coreitems = {}
94 94
95 95 def _register(configtable, *args, **kwargs):
96 96 item = configitem(*args, **kwargs)
97 97 section = configtable.setdefault(item.section, itemregister())
98 98 if item.name in section:
99 99 msg = "duplicated config item registration for '%s.%s'"
100 100 raise error.ProgrammingError(msg % (item.section, item.name))
101 101 section[item.name] = item
102 102
103 103 # special value for case where the default is derived from other values
104 104 dynamicdefault = object()
105 105
106 106 # Registering actual config items
107 107
108 108 def getitemregister(configtable):
109 109 f = functools.partial(_register, configtable)
110 110 # export pseudo enum as configitem.*
111 111 f.dynamicdefault = dynamicdefault
112 112 return f
113 113
114 114 coreconfigitem = getitemregister(coreitems)
115 115
116 116 def _registerdiffopts(section, configprefix=''):
117 117 coreconfigitem(section, configprefix + 'nodates',
118 118 default=False,
119 119 )
120 120 coreconfigitem(section, configprefix + 'showfunc',
121 121 default=False,
122 122 )
123 123 coreconfigitem(section, configprefix + 'unified',
124 124 default=None,
125 125 )
126 126 coreconfigitem(section, configprefix + 'git',
127 127 default=False,
128 128 )
129 129 coreconfigitem(section, configprefix + 'ignorews',
130 130 default=False,
131 131 )
132 132 coreconfigitem(section, configprefix + 'ignorewsamount',
133 133 default=False,
134 134 )
135 135 coreconfigitem(section, configprefix + 'ignoreblanklines',
136 136 default=False,
137 137 )
138 138 coreconfigitem(section, configprefix + 'ignorewseol',
139 139 default=False,
140 140 )
141 141 coreconfigitem(section, configprefix + 'nobinary',
142 142 default=False,
143 143 )
144 144 coreconfigitem(section, configprefix + 'noprefix',
145 145 default=False,
146 146 )
147 147 coreconfigitem(section, configprefix + 'word-diff',
148 148 default=False,
149 149 )
150 150
151 151 coreconfigitem('alias', '.*',
152 152 default=dynamicdefault,
153 153 generic=True,
154 154 )
155 155 coreconfigitem('auth', 'cookiefile',
156 156 default=None,
157 157 )
158 158 _registerdiffopts(section='annotate')
159 159 # bookmarks.pushing: internal hack for discovery
160 160 coreconfigitem('bookmarks', 'pushing',
161 161 default=list,
162 162 )
163 163 # bundle.mainreporoot: internal hack for bundlerepo
164 164 coreconfigitem('bundle', 'mainreporoot',
165 165 default='',
166 166 )
167 167 coreconfigitem('censor', 'policy',
168 168 default='abort',
169 169 )
170 170 coreconfigitem('chgserver', 'idletimeout',
171 171 default=3600,
172 172 )
173 173 coreconfigitem('chgserver', 'skiphash',
174 174 default=False,
175 175 )
176 176 coreconfigitem('cmdserver', 'log',
177 177 default=None,
178 178 )
179 179 coreconfigitem('cmdserver', 'max-log-files',
180 180 default=7,
181 181 )
182 182 coreconfigitem('cmdserver', 'max-log-size',
183 183 default='1 MB',
184 184 )
185 185 coreconfigitem('cmdserver', 'max-repo-cache',
186 186 default=0,
187 187 )
188 188 coreconfigitem('cmdserver', 'message-encodings',
189 189 default=list,
190 190 )
191 191 coreconfigitem('cmdserver', 'track-log',
192 192 default=lambda: ['chgserver', 'cmdserver', 'repocache'],
193 193 )
194 194 coreconfigitem('color', '.*',
195 195 default=None,
196 196 generic=True,
197 197 )
198 198 coreconfigitem('color', 'mode',
199 199 default='auto',
200 200 )
201 201 coreconfigitem('color', 'pagermode',
202 202 default=dynamicdefault,
203 203 )
204 204 _registerdiffopts(section='commands', configprefix='commit.interactive.')
205 205 coreconfigitem('commands', 'commit.post-status',
206 206 default=False,
207 207 )
208 208 coreconfigitem('commands', 'grep.all-files',
209 209 default=False,
210 210 )
211 211 coreconfigitem('commands', 'resolve.confirm',
212 212 default=False,
213 213 )
214 214 coreconfigitem('commands', 'resolve.explicit-re-merge',
215 215 default=False,
216 216 )
217 217 coreconfigitem('commands', 'resolve.mark-check',
218 218 default='none',
219 219 )
220 220 _registerdiffopts(section='commands', configprefix='revert.interactive.')
221 221 coreconfigitem('commands', 'show.aliasprefix',
222 222 default=list,
223 223 )
224 224 coreconfigitem('commands', 'status.relative',
225 225 default=False,
226 226 )
227 227 coreconfigitem('commands', 'status.skipstates',
228 228 default=[],
229 229 )
230 230 coreconfigitem('commands', 'status.terse',
231 231 default='',
232 232 )
233 233 coreconfigitem('commands', 'status.verbose',
234 234 default=False,
235 235 )
236 236 coreconfigitem('commands', 'update.check',
237 237 default=None,
238 238 )
239 239 coreconfigitem('commands', 'update.requiredest',
240 240 default=False,
241 241 )
242 242 coreconfigitem('committemplate', '.*',
243 243 default=None,
244 244 generic=True,
245 245 )
246 246 coreconfigitem('convert', 'bzr.saverev',
247 247 default=True,
248 248 )
249 249 coreconfigitem('convert', 'cvsps.cache',
250 250 default=True,
251 251 )
252 252 coreconfigitem('convert', 'cvsps.fuzz',
253 253 default=60,
254 254 )
255 255 coreconfigitem('convert', 'cvsps.logencoding',
256 256 default=None,
257 257 )
258 258 coreconfigitem('convert', 'cvsps.mergefrom',
259 259 default=None,
260 260 )
261 261 coreconfigitem('convert', 'cvsps.mergeto',
262 262 default=None,
263 263 )
264 264 coreconfigitem('convert', 'git.committeractions',
265 265 default=lambda: ['messagedifferent'],
266 266 )
267 267 coreconfigitem('convert', 'git.extrakeys',
268 268 default=list,
269 269 )
270 270 coreconfigitem('convert', 'git.findcopiesharder',
271 271 default=False,
272 272 )
273 273 coreconfigitem('convert', 'git.remoteprefix',
274 274 default='remote',
275 275 )
276 276 coreconfigitem('convert', 'git.renamelimit',
277 277 default=400,
278 278 )
279 279 coreconfigitem('convert', 'git.saverev',
280 280 default=True,
281 281 )
282 282 coreconfigitem('convert', 'git.similarity',
283 283 default=50,
284 284 )
285 285 coreconfigitem('convert', 'git.skipsubmodules',
286 286 default=False,
287 287 )
288 288 coreconfigitem('convert', 'hg.clonebranches',
289 289 default=False,
290 290 )
291 291 coreconfigitem('convert', 'hg.ignoreerrors',
292 292 default=False,
293 293 )
294 294 coreconfigitem('convert', 'hg.revs',
295 295 default=None,
296 296 )
297 297 coreconfigitem('convert', 'hg.saverev',
298 298 default=False,
299 299 )
300 300 coreconfigitem('convert', 'hg.sourcename',
301 301 default=None,
302 302 )
303 303 coreconfigitem('convert', 'hg.startrev',
304 304 default=None,
305 305 )
306 306 coreconfigitem('convert', 'hg.tagsbranch',
307 307 default='default',
308 308 )
309 309 coreconfigitem('convert', 'hg.usebranchnames',
310 310 default=True,
311 311 )
312 312 coreconfigitem('convert', 'ignoreancestorcheck',
313 313 default=False,
314 314 )
315 315 coreconfigitem('convert', 'localtimezone',
316 316 default=False,
317 317 )
318 318 coreconfigitem('convert', 'p4.encoding',
319 319 default=dynamicdefault,
320 320 )
321 321 coreconfigitem('convert', 'p4.startrev',
322 322 default=0,
323 323 )
324 324 coreconfigitem('convert', 'skiptags',
325 325 default=False,
326 326 )
327 327 coreconfigitem('convert', 'svn.debugsvnlog',
328 328 default=True,
329 329 )
330 330 coreconfigitem('convert', 'svn.trunk',
331 331 default=None,
332 332 )
333 333 coreconfigitem('convert', 'svn.tags',
334 334 default=None,
335 335 )
336 336 coreconfigitem('convert', 'svn.branches',
337 337 default=None,
338 338 )
339 339 coreconfigitem('convert', 'svn.startrev',
340 340 default=0,
341 341 )
342 342 coreconfigitem('debug', 'dirstate.delaywrite',
343 343 default=0,
344 344 )
345 345 coreconfigitem('defaults', '.*',
346 346 default=None,
347 347 generic=True,
348 348 )
349 349 coreconfigitem('devel', 'all-warnings',
350 350 default=False,
351 351 )
352 352 coreconfigitem('devel', 'bundle2.debug',
353 353 default=False,
354 354 )
355 355 coreconfigitem('devel', 'bundle.delta',
356 356 default='',
357 357 )
358 358 coreconfigitem('devel', 'cache-vfs',
359 359 default=None,
360 360 )
361 361 coreconfigitem('devel', 'check-locks',
362 362 default=False,
363 363 )
364 364 coreconfigitem('devel', 'check-relroot',
365 365 default=False,
366 366 )
367 367 coreconfigitem('devel', 'default-date',
368 368 default=None,
369 369 )
370 370 coreconfigitem('devel', 'deprec-warn',
371 371 default=False,
372 372 )
373 373 coreconfigitem('devel', 'disableloaddefaultcerts',
374 374 default=False,
375 375 )
376 376 coreconfigitem('devel', 'warn-empty-changegroup',
377 377 default=False,
378 378 )
379 379 coreconfigitem('devel', 'legacy.exchange',
380 380 default=list,
381 381 )
382 382 coreconfigitem('devel', 'servercafile',
383 383 default='',
384 384 )
385 385 coreconfigitem('devel', 'serverexactprotocol',
386 386 default='',
387 387 )
388 388 coreconfigitem('devel', 'serverrequirecert',
389 389 default=False,
390 390 )
391 391 coreconfigitem('devel', 'strip-obsmarkers',
392 392 default=True,
393 393 )
394 394 coreconfigitem('devel', 'warn-config',
395 395 default=None,
396 396 )
397 397 coreconfigitem('devel', 'warn-config-default',
398 398 default=None,
399 399 )
400 400 coreconfigitem('devel', 'user.obsmarker',
401 401 default=None,
402 402 )
403 403 coreconfigitem('devel', 'warn-config-unknown',
404 404 default=None,
405 405 )
406 406 coreconfigitem('devel', 'debug.copies',
407 407 default=False,
408 408 )
409 409 coreconfigitem('devel', 'debug.extensions',
410 410 default=False,
411 411 )
412 412 coreconfigitem('devel', 'debug.peer-request',
413 413 default=False,
414 414 )
415 415 _registerdiffopts(section='diff')
416 416 coreconfigitem('email', 'bcc',
417 417 default=None,
418 418 )
419 419 coreconfigitem('email', 'cc',
420 420 default=None,
421 421 )
422 422 coreconfigitem('email', 'charsets',
423 423 default=list,
424 424 )
425 425 coreconfigitem('email', 'from',
426 426 default=None,
427 427 )
428 428 coreconfigitem('email', 'method',
429 429 default='smtp',
430 430 )
431 431 coreconfigitem('email', 'reply-to',
432 432 default=None,
433 433 )
434 434 coreconfigitem('email', 'to',
435 435 default=None,
436 436 )
437 437 coreconfigitem('experimental', 'archivemetatemplate',
438 438 default=dynamicdefault,
439 439 )
440 440 coreconfigitem('experimental', 'auto-publish',
441 441 default='publish',
442 442 )
443 443 coreconfigitem('experimental', 'bundle-phases',
444 444 default=False,
445 445 )
446 446 coreconfigitem('experimental', 'bundle2-advertise',
447 447 default=True,
448 448 )
449 449 coreconfigitem('experimental', 'bundle2-output-capture',
450 450 default=False,
451 451 )
452 452 coreconfigitem('experimental', 'bundle2.pushback',
453 453 default=False,
454 454 )
455 455 coreconfigitem('experimental', 'bundle2lazylocking',
456 456 default=False,
457 457 )
458 458 coreconfigitem('experimental', 'bundlecomplevel',
459 459 default=None,
460 460 )
461 461 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
462 462 default=None,
463 463 )
464 464 coreconfigitem('experimental', 'bundlecomplevel.gzip',
465 465 default=None,
466 466 )
467 467 coreconfigitem('experimental', 'bundlecomplevel.none',
468 468 default=None,
469 469 )
470 470 coreconfigitem('experimental', 'bundlecomplevel.zstd',
471 471 default=None,
472 472 )
473 473 coreconfigitem('experimental', 'changegroup3',
474 474 default=False,
475 475 )
476 476 coreconfigitem('experimental', 'cleanup-as-archived',
477 477 default=False,
478 478 )
479 479 coreconfigitem('experimental', 'clientcompressionengines',
480 480 default=list,
481 481 )
482 482 coreconfigitem('experimental', 'copytrace',
483 483 default='on',
484 484 )
485 485 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
486 486 default=100,
487 487 )
488 488 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
489 489 default=100,
490 490 )
491 491 coreconfigitem('experimental', 'copies.read-from',
492 492 default="filelog-only",
493 493 )
494 494 coreconfigitem('experimental', 'copies.write-to',
495 495 default='filelog-only',
496 496 )
497 497 coreconfigitem('experimental', 'crecordtest',
498 498 default=None,
499 499 )
500 500 coreconfigitem('experimental', 'directaccess',
501 501 default=False,
502 502 )
503 503 coreconfigitem('experimental', 'directaccess.revnums',
504 504 default=False,
505 505 )
506 506 coreconfigitem('experimental', 'editortmpinhg',
507 507 default=False,
508 508 )
509 509 coreconfigitem('experimental', 'evolution',
510 510 default=list,
511 511 )
512 512 coreconfigitem('experimental', 'evolution.allowdivergence',
513 513 default=False,
514 514 alias=[('experimental', 'allowdivergence')]
515 515 )
516 516 coreconfigitem('experimental', 'evolution.allowunstable',
517 517 default=None,
518 518 )
519 519 coreconfigitem('experimental', 'evolution.createmarkers',
520 520 default=None,
521 521 )
522 522 coreconfigitem('experimental', 'evolution.effect-flags',
523 523 default=True,
524 524 alias=[('experimental', 'effect-flags')]
525 525 )
526 526 coreconfigitem('experimental', 'evolution.exchange',
527 527 default=None,
528 528 )
529 529 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
530 530 default=False,
531 531 )
532 coreconfigitem('experimental', 'log.topo',
533 default=False,
534 )
532 535 coreconfigitem('experimental', 'evolution.report-instabilities',
533 536 default=True,
534 537 )
535 538 coreconfigitem('experimental', 'evolution.track-operation',
536 539 default=True,
537 540 )
538 541 # repo-level config to exclude a revset visibility
539 542 #
540 543 # The target use case is to use `share` to expose different subset of the same
541 544 # repository, especially server side. See also `server.view`.
542 545 coreconfigitem('experimental', 'extra-filter-revs',
543 546 default=None,
544 547 )
545 548 coreconfigitem('experimental', 'maxdeltachainspan',
546 549 default=-1,
547 550 )
548 551 coreconfigitem('experimental', 'mergetempdirprefix',
549 552 default=None,
550 553 )
551 554 coreconfigitem('experimental', 'mmapindexthreshold',
552 555 default=None,
553 556 )
554 557 coreconfigitem('experimental', 'narrow',
555 558 default=False,
556 559 )
557 560 coreconfigitem('experimental', 'nonnormalparanoidcheck',
558 561 default=False,
559 562 )
560 563 coreconfigitem('experimental', 'exportableenviron',
561 564 default=list,
562 565 )
563 566 coreconfigitem('experimental', 'extendedheader.index',
564 567 default=None,
565 568 )
566 569 coreconfigitem('experimental', 'extendedheader.similarity',
567 570 default=False,
568 571 )
569 572 coreconfigitem('experimental', 'graphshorten',
570 573 default=False,
571 574 )
572 575 coreconfigitem('experimental', 'graphstyle.parent',
573 576 default=dynamicdefault,
574 577 )
575 578 coreconfigitem('experimental', 'graphstyle.missing',
576 579 default=dynamicdefault,
577 580 )
578 581 coreconfigitem('experimental', 'graphstyle.grandparent',
579 582 default=dynamicdefault,
580 583 )
581 584 coreconfigitem('experimental', 'hook-track-tags',
582 585 default=False,
583 586 )
584 587 coreconfigitem('experimental', 'httppeer.advertise-v2',
585 588 default=False,
586 589 )
587 590 coreconfigitem('experimental', 'httppeer.v2-encoder-order',
588 591 default=None,
589 592 )
590 593 coreconfigitem('experimental', 'httppostargs',
591 594 default=False,
592 595 )
593 596 coreconfigitem('experimental', 'mergedriver',
594 597 default=None,
595 598 )
596 599 coreconfigitem('experimental', 'nointerrupt', default=False)
597 600 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
598 601
599 602 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
600 603 default=False,
601 604 )
602 605 coreconfigitem('experimental', 'remotenames',
603 606 default=False,
604 607 )
605 608 coreconfigitem('experimental', 'removeemptydirs',
606 609 default=True,
607 610 )
608 611 coreconfigitem('experimental', 'revert.interactive.select-to-keep',
609 612 default=False,
610 613 )
611 614 coreconfigitem('experimental', 'revisions.prefixhexnode',
612 615 default=False,
613 616 )
614 617 coreconfigitem('experimental', 'revlogv2',
615 618 default=None,
616 619 )
617 620 coreconfigitem('experimental', 'revisions.disambiguatewithin',
618 621 default=None,
619 622 )
620 623 coreconfigitem('experimental', 'server.filesdata.recommended-batch-size',
621 624 default=50000,
622 625 )
623 626 coreconfigitem('experimental', 'server.manifestdata.recommended-batch-size',
624 627 default=100000,
625 628 )
626 629 coreconfigitem('experimental', 'server.stream-narrow-clones',
627 630 default=False,
628 631 )
629 632 coreconfigitem('experimental', 'single-head-per-branch',
630 633 default=False,
631 634 )
632 635 coreconfigitem('experimental', 'sshserver.support-v2',
633 636 default=False,
634 637 )
635 638 coreconfigitem('experimental', 'sparse-read',
636 639 default=False,
637 640 )
638 641 coreconfigitem('experimental', 'sparse-read.density-threshold',
639 642 default=0.50,
640 643 )
641 644 coreconfigitem('experimental', 'sparse-read.min-gap-size',
642 645 default='65K',
643 646 )
644 647 coreconfigitem('experimental', 'treemanifest',
645 648 default=False,
646 649 )
647 650 coreconfigitem('experimental', 'update.atomic-file',
648 651 default=False,
649 652 )
650 653 coreconfigitem('experimental', 'sshpeer.advertise-v2',
651 654 default=False,
652 655 )
653 656 coreconfigitem('experimental', 'web.apiserver',
654 657 default=False,
655 658 )
656 659 coreconfigitem('experimental', 'web.api.http-v2',
657 660 default=False,
658 661 )
659 662 coreconfigitem('experimental', 'web.api.debugreflect',
660 663 default=False,
661 664 )
662 665 coreconfigitem('experimental', 'worker.wdir-get-thread-safe',
663 666 default=False,
664 667 )
665 668 coreconfigitem('experimental', 'xdiff',
666 669 default=False,
667 670 )
668 671 coreconfigitem('extensions', '.*',
669 672 default=None,
670 673 generic=True,
671 674 )
672 675 coreconfigitem('extdata', '.*',
673 676 default=None,
674 677 generic=True,
675 678 )
676 679 coreconfigitem('format', 'chunkcachesize',
677 680 default=None,
678 681 )
679 682 coreconfigitem('format', 'dotencode',
680 683 default=True,
681 684 )
682 685 coreconfigitem('format', 'generaldelta',
683 686 default=False,
684 687 )
685 688 coreconfigitem('format', 'manifestcachesize',
686 689 default=None,
687 690 )
688 691 coreconfigitem('format', 'maxchainlen',
689 692 default=dynamicdefault,
690 693 )
691 694 coreconfigitem('format', 'obsstore-version',
692 695 default=None,
693 696 )
694 697 coreconfigitem('format', 'sparse-revlog',
695 698 default=True,
696 699 )
697 700 coreconfigitem('format', 'revlog-compression',
698 701 default='zlib',
699 702 alias=[('experimental', 'format.compression')]
700 703 )
701 704 coreconfigitem('format', 'usefncache',
702 705 default=True,
703 706 )
704 707 coreconfigitem('format', 'usegeneraldelta',
705 708 default=True,
706 709 )
707 710 coreconfigitem('format', 'usestore',
708 711 default=True,
709 712 )
710 713 coreconfigitem('format', 'internal-phase',
711 714 default=False,
712 715 )
713 716 coreconfigitem('fsmonitor', 'warn_when_unused',
714 717 default=True,
715 718 )
716 719 coreconfigitem('fsmonitor', 'warn_update_file_count',
717 720 default=50000,
718 721 )
719 722 coreconfigitem('help', br'hidden-command\..*',
720 723 default=False,
721 724 generic=True,
722 725 )
723 726 coreconfigitem('help', br'hidden-topic\..*',
724 727 default=False,
725 728 generic=True,
726 729 )
727 730 coreconfigitem('hooks', '.*',
728 731 default=dynamicdefault,
729 732 generic=True,
730 733 )
731 734 coreconfigitem('hgweb-paths', '.*',
732 735 default=list,
733 736 generic=True,
734 737 )
735 738 coreconfigitem('hostfingerprints', '.*',
736 739 default=list,
737 740 generic=True,
738 741 )
739 742 coreconfigitem('hostsecurity', 'ciphers',
740 743 default=None,
741 744 )
742 745 coreconfigitem('hostsecurity', 'disabletls10warning',
743 746 default=False,
744 747 )
745 748 coreconfigitem('hostsecurity', 'minimumprotocol',
746 749 default=dynamicdefault,
747 750 )
748 751 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
749 752 default=dynamicdefault,
750 753 generic=True,
751 754 )
752 755 coreconfigitem('hostsecurity', '.*:ciphers$',
753 756 default=dynamicdefault,
754 757 generic=True,
755 758 )
756 759 coreconfigitem('hostsecurity', '.*:fingerprints$',
757 760 default=list,
758 761 generic=True,
759 762 )
760 763 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
761 764 default=None,
762 765 generic=True,
763 766 )
764 767
765 768 coreconfigitem('http_proxy', 'always',
766 769 default=False,
767 770 )
768 771 coreconfigitem('http_proxy', 'host',
769 772 default=None,
770 773 )
771 774 coreconfigitem('http_proxy', 'no',
772 775 default=list,
773 776 )
774 777 coreconfigitem('http_proxy', 'passwd',
775 778 default=None,
776 779 )
777 780 coreconfigitem('http_proxy', 'user',
778 781 default=None,
779 782 )
780 783
781 784 coreconfigitem('http', 'timeout',
782 785 default=None,
783 786 )
784 787
785 788 coreconfigitem('logtoprocess', 'commandexception',
786 789 default=None,
787 790 )
788 791 coreconfigitem('logtoprocess', 'commandfinish',
789 792 default=None,
790 793 )
791 794 coreconfigitem('logtoprocess', 'command',
792 795 default=None,
793 796 )
794 797 coreconfigitem('logtoprocess', 'develwarn',
795 798 default=None,
796 799 )
797 800 coreconfigitem('logtoprocess', 'uiblocked',
798 801 default=None,
799 802 )
800 803 coreconfigitem('merge', 'checkunknown',
801 804 default='abort',
802 805 )
803 806 coreconfigitem('merge', 'checkignored',
804 807 default='abort',
805 808 )
806 809 coreconfigitem('experimental', 'merge.checkpathconflicts',
807 810 default=False,
808 811 )
809 812 coreconfigitem('merge', 'followcopies',
810 813 default=True,
811 814 )
812 815 coreconfigitem('merge', 'on-failure',
813 816 default='continue',
814 817 )
815 818 coreconfigitem('merge', 'preferancestor',
816 819 default=lambda: ['*'],
817 820 )
818 821 coreconfigitem('merge', 'strict-capability-check',
819 822 default=False,
820 823 )
821 824 coreconfigitem('merge-tools', '.*',
822 825 default=None,
823 826 generic=True,
824 827 )
825 828 coreconfigitem('merge-tools', br'.*\.args$',
826 829 default="$local $base $other",
827 830 generic=True,
828 831 priority=-1,
829 832 )
830 833 coreconfigitem('merge-tools', br'.*\.binary$',
831 834 default=False,
832 835 generic=True,
833 836 priority=-1,
834 837 )
835 838 coreconfigitem('merge-tools', br'.*\.check$',
836 839 default=list,
837 840 generic=True,
838 841 priority=-1,
839 842 )
840 843 coreconfigitem('merge-tools', br'.*\.checkchanged$',
841 844 default=False,
842 845 generic=True,
843 846 priority=-1,
844 847 )
845 848 coreconfigitem('merge-tools', br'.*\.executable$',
846 849 default=dynamicdefault,
847 850 generic=True,
848 851 priority=-1,
849 852 )
850 853 coreconfigitem('merge-tools', br'.*\.fixeol$',
851 854 default=False,
852 855 generic=True,
853 856 priority=-1,
854 857 )
855 858 coreconfigitem('merge-tools', br'.*\.gui$',
856 859 default=False,
857 860 generic=True,
858 861 priority=-1,
859 862 )
860 863 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
861 864 default='basic',
862 865 generic=True,
863 866 priority=-1,
864 867 )
865 868 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
866 869 default=dynamicdefault, # take from ui.mergemarkertemplate
867 870 generic=True,
868 871 priority=-1,
869 872 )
870 873 coreconfigitem('merge-tools', br'.*\.priority$',
871 874 default=0,
872 875 generic=True,
873 876 priority=-1,
874 877 )
875 878 coreconfigitem('merge-tools', br'.*\.premerge$',
876 879 default=dynamicdefault,
877 880 generic=True,
878 881 priority=-1,
879 882 )
880 883 coreconfigitem('merge-tools', br'.*\.symlink$',
881 884 default=False,
882 885 generic=True,
883 886 priority=-1,
884 887 )
885 888 coreconfigitem('pager', 'attend-.*',
886 889 default=dynamicdefault,
887 890 generic=True,
888 891 )
889 892 coreconfigitem('pager', 'ignore',
890 893 default=list,
891 894 )
892 895 coreconfigitem('pager', 'pager',
893 896 default=dynamicdefault,
894 897 )
895 898 coreconfigitem('patch', 'eol',
896 899 default='strict',
897 900 )
898 901 coreconfigitem('patch', 'fuzz',
899 902 default=2,
900 903 )
901 904 coreconfigitem('paths', 'default',
902 905 default=None,
903 906 )
904 907 coreconfigitem('paths', 'default-push',
905 908 default=None,
906 909 )
907 910 coreconfigitem('paths', '.*',
908 911 default=None,
909 912 generic=True,
910 913 )
911 914 coreconfigitem('phases', 'checksubrepos',
912 915 default='follow',
913 916 )
914 917 coreconfigitem('phases', 'new-commit',
915 918 default='draft',
916 919 )
917 920 coreconfigitem('phases', 'publish',
918 921 default=True,
919 922 )
920 923 coreconfigitem('profiling', 'enabled',
921 924 default=False,
922 925 )
923 926 coreconfigitem('profiling', 'format',
924 927 default='text',
925 928 )
926 929 coreconfigitem('profiling', 'freq',
927 930 default=1000,
928 931 )
929 932 coreconfigitem('profiling', 'limit',
930 933 default=30,
931 934 )
932 935 coreconfigitem('profiling', 'nested',
933 936 default=0,
934 937 )
935 938 coreconfigitem('profiling', 'output',
936 939 default=None,
937 940 )
938 941 coreconfigitem('profiling', 'showmax',
939 942 default=0.999,
940 943 )
941 944 coreconfigitem('profiling', 'showmin',
942 945 default=dynamicdefault,
943 946 )
944 947 coreconfigitem('profiling', 'sort',
945 948 default='inlinetime',
946 949 )
947 950 coreconfigitem('profiling', 'statformat',
948 951 default='hotpath',
949 952 )
950 953 coreconfigitem('profiling', 'time-track',
951 954 default=dynamicdefault,
952 955 )
953 956 coreconfigitem('profiling', 'type',
954 957 default='stat',
955 958 )
956 959 coreconfigitem('progress', 'assume-tty',
957 960 default=False,
958 961 )
959 962 coreconfigitem('progress', 'changedelay',
960 963 default=1,
961 964 )
962 965 coreconfigitem('progress', 'clear-complete',
963 966 default=True,
964 967 )
965 968 coreconfigitem('progress', 'debug',
966 969 default=False,
967 970 )
968 971 coreconfigitem('progress', 'delay',
969 972 default=3,
970 973 )
971 974 coreconfigitem('progress', 'disable',
972 975 default=False,
973 976 )
974 977 coreconfigitem('progress', 'estimateinterval',
975 978 default=60.0,
976 979 )
977 980 coreconfigitem('progress', 'format',
978 981 default=lambda: ['topic', 'bar', 'number', 'estimate'],
979 982 )
980 983 coreconfigitem('progress', 'refresh',
981 984 default=0.1,
982 985 )
983 986 coreconfigitem('progress', 'width',
984 987 default=dynamicdefault,
985 988 )
986 989 coreconfigitem('push', 'pushvars.server',
987 990 default=False,
988 991 )
989 992 coreconfigitem('rewrite', 'backup-bundle',
990 993 default=True,
991 994 alias=[('ui', 'history-editing-backup')],
992 995 )
993 996 coreconfigitem('rewrite', 'update-timestamp',
994 997 default=False,
995 998 )
996 999 coreconfigitem('storage', 'new-repo-backend',
997 1000 default='revlogv1',
998 1001 )
999 1002 coreconfigitem('storage', 'revlog.optimize-delta-parent-choice',
1000 1003 default=True,
1001 1004 alias=[('format', 'aggressivemergedeltas')],
1002 1005 )
1003 1006 coreconfigitem('storage', 'revlog.reuse-external-delta',
1004 1007 default=True,
1005 1008 )
1006 1009 coreconfigitem('storage', 'revlog.reuse-external-delta-parent',
1007 1010 default=None,
1008 1011 )
1009 1012 coreconfigitem('storage', 'revlog.zlib.level',
1010 1013 default=None,
1011 1014 )
1012 1015 coreconfigitem('storage', 'revlog.zstd.level',
1013 1016 default=None,
1014 1017 )
1015 1018 coreconfigitem('server', 'bookmarks-pushkey-compat',
1016 1019 default=True,
1017 1020 )
1018 1021 coreconfigitem('server', 'bundle1',
1019 1022 default=True,
1020 1023 )
1021 1024 coreconfigitem('server', 'bundle1gd',
1022 1025 default=None,
1023 1026 )
1024 1027 coreconfigitem('server', 'bundle1.pull',
1025 1028 default=None,
1026 1029 )
1027 1030 coreconfigitem('server', 'bundle1gd.pull',
1028 1031 default=None,
1029 1032 )
1030 1033 coreconfigitem('server', 'bundle1.push',
1031 1034 default=None,
1032 1035 )
1033 1036 coreconfigitem('server', 'bundle1gd.push',
1034 1037 default=None,
1035 1038 )
1036 1039 coreconfigitem('server', 'bundle2.stream',
1037 1040 default=True,
1038 1041 alias=[('experimental', 'bundle2.stream')]
1039 1042 )
1040 1043 coreconfigitem('server', 'compressionengines',
1041 1044 default=list,
1042 1045 )
1043 1046 coreconfigitem('server', 'concurrent-push-mode',
1044 1047 default='strict',
1045 1048 )
1046 1049 coreconfigitem('server', 'disablefullbundle',
1047 1050 default=False,
1048 1051 )
1049 1052 coreconfigitem('server', 'maxhttpheaderlen',
1050 1053 default=1024,
1051 1054 )
1052 1055 coreconfigitem('server', 'pullbundle',
1053 1056 default=False,
1054 1057 )
1055 1058 coreconfigitem('server', 'preferuncompressed',
1056 1059 default=False,
1057 1060 )
1058 1061 coreconfigitem('server', 'streamunbundle',
1059 1062 default=False,
1060 1063 )
1061 1064 coreconfigitem('server', 'uncompressed',
1062 1065 default=True,
1063 1066 )
1064 1067 coreconfigitem('server', 'uncompressedallowsecret',
1065 1068 default=False,
1066 1069 )
1067 1070 coreconfigitem('server', 'view',
1068 1071 default='served',
1069 1072 )
1070 1073 coreconfigitem('server', 'validate',
1071 1074 default=False,
1072 1075 )
1073 1076 coreconfigitem('server', 'zliblevel',
1074 1077 default=-1,
1075 1078 )
1076 1079 coreconfigitem('server', 'zstdlevel',
1077 1080 default=3,
1078 1081 )
1079 1082 coreconfigitem('share', 'pool',
1080 1083 default=None,
1081 1084 )
1082 1085 coreconfigitem('share', 'poolnaming',
1083 1086 default='identity',
1084 1087 )
1085 1088 coreconfigitem('smtp', 'host',
1086 1089 default=None,
1087 1090 )
1088 1091 coreconfigitem('smtp', 'local_hostname',
1089 1092 default=None,
1090 1093 )
1091 1094 coreconfigitem('smtp', 'password',
1092 1095 default=None,
1093 1096 )
1094 1097 coreconfigitem('smtp', 'port',
1095 1098 default=dynamicdefault,
1096 1099 )
1097 1100 coreconfigitem('smtp', 'tls',
1098 1101 default='none',
1099 1102 )
1100 1103 coreconfigitem('smtp', 'username',
1101 1104 default=None,
1102 1105 )
1103 1106 coreconfigitem('sparse', 'missingwarning',
1104 1107 default=True,
1105 1108 )
1106 1109 coreconfigitem('subrepos', 'allowed',
1107 1110 default=dynamicdefault, # to make backporting simpler
1108 1111 )
1109 1112 coreconfigitem('subrepos', 'hg:allowed',
1110 1113 default=dynamicdefault,
1111 1114 )
1112 1115 coreconfigitem('subrepos', 'git:allowed',
1113 1116 default=dynamicdefault,
1114 1117 )
1115 1118 coreconfigitem('subrepos', 'svn:allowed',
1116 1119 default=dynamicdefault,
1117 1120 )
1118 1121 coreconfigitem('templates', '.*',
1119 1122 default=None,
1120 1123 generic=True,
1121 1124 )
1122 1125 coreconfigitem('templateconfig', '.*',
1123 1126 default=dynamicdefault,
1124 1127 generic=True,
1125 1128 )
1126 1129 coreconfigitem('trusted', 'groups',
1127 1130 default=list,
1128 1131 )
1129 1132 coreconfigitem('trusted', 'users',
1130 1133 default=list,
1131 1134 )
1132 1135 coreconfigitem('ui', '_usedassubrepo',
1133 1136 default=False,
1134 1137 )
1135 1138 coreconfigitem('ui', 'allowemptycommit',
1136 1139 default=False,
1137 1140 )
1138 1141 coreconfigitem('ui', 'archivemeta',
1139 1142 default=True,
1140 1143 )
1141 1144 coreconfigitem('ui', 'askusername',
1142 1145 default=False,
1143 1146 )
1144 1147 coreconfigitem('ui', 'clonebundlefallback',
1145 1148 default=False,
1146 1149 )
1147 1150 coreconfigitem('ui', 'clonebundleprefers',
1148 1151 default=list,
1149 1152 )
1150 1153 coreconfigitem('ui', 'clonebundles',
1151 1154 default=True,
1152 1155 )
1153 1156 coreconfigitem('ui', 'color',
1154 1157 default='auto',
1155 1158 )
1156 1159 coreconfigitem('ui', 'commitsubrepos',
1157 1160 default=False,
1158 1161 )
1159 1162 coreconfigitem('ui', 'debug',
1160 1163 default=False,
1161 1164 )
1162 1165 coreconfigitem('ui', 'debugger',
1163 1166 default=None,
1164 1167 )
1165 1168 coreconfigitem('ui', 'editor',
1166 1169 default=dynamicdefault,
1167 1170 )
1168 1171 coreconfigitem('ui', 'fallbackencoding',
1169 1172 default=None,
1170 1173 )
1171 1174 coreconfigitem('ui', 'forcecwd',
1172 1175 default=None,
1173 1176 )
1174 1177 coreconfigitem('ui', 'forcemerge',
1175 1178 default=None,
1176 1179 )
1177 1180 coreconfigitem('ui', 'formatdebug',
1178 1181 default=False,
1179 1182 )
1180 1183 coreconfigitem('ui', 'formatjson',
1181 1184 default=False,
1182 1185 )
1183 1186 coreconfigitem('ui', 'formatted',
1184 1187 default=None,
1185 1188 )
1186 1189 coreconfigitem('ui', 'graphnodetemplate',
1187 1190 default=None,
1188 1191 )
1189 1192 coreconfigitem('ui', 'interactive',
1190 1193 default=None,
1191 1194 )
1192 1195 coreconfigitem('ui', 'interface',
1193 1196 default=None,
1194 1197 )
1195 1198 coreconfigitem('ui', 'interface.chunkselector',
1196 1199 default=None,
1197 1200 )
1198 1201 coreconfigitem('ui', 'large-file-limit',
1199 1202 default=10000000,
1200 1203 )
1201 1204 coreconfigitem('ui', 'logblockedtimes',
1202 1205 default=False,
1203 1206 )
1204 1207 coreconfigitem('ui', 'logtemplate',
1205 1208 default=None,
1206 1209 )
1207 1210 coreconfigitem('ui', 'merge',
1208 1211 default=None,
1209 1212 )
1210 1213 coreconfigitem('ui', 'mergemarkers',
1211 1214 default='basic',
1212 1215 )
1213 1216 coreconfigitem('ui', 'mergemarkertemplate',
1214 1217 default=('{node|short} '
1215 1218 '{ifeq(tags, "tip", "", '
1216 1219 'ifeq(tags, "", "", "{tags} "))}'
1217 1220 '{if(bookmarks, "{bookmarks} ")}'
1218 1221 '{ifeq(branch, "default", "", "{branch} ")}'
1219 1222 '- {author|user}: {desc|firstline}')
1220 1223 )
1221 1224 coreconfigitem('ui', 'message-output',
1222 1225 default='stdio',
1223 1226 )
1224 1227 coreconfigitem('ui', 'nontty',
1225 1228 default=False,
1226 1229 )
1227 1230 coreconfigitem('ui', 'origbackuppath',
1228 1231 default=None,
1229 1232 )
1230 1233 coreconfigitem('ui', 'paginate',
1231 1234 default=True,
1232 1235 )
1233 1236 coreconfigitem('ui', 'patch',
1234 1237 default=None,
1235 1238 )
1236 1239 coreconfigitem('ui', 'pre-merge-tool-output-template',
1237 1240 default=None,
1238 1241 )
1239 1242 coreconfigitem('ui', 'portablefilenames',
1240 1243 default='warn',
1241 1244 )
1242 1245 coreconfigitem('ui', 'promptecho',
1243 1246 default=False,
1244 1247 )
1245 1248 coreconfigitem('ui', 'quiet',
1246 1249 default=False,
1247 1250 )
1248 1251 coreconfigitem('ui', 'quietbookmarkmove',
1249 1252 default=False,
1250 1253 )
1251 1254 coreconfigitem('ui', 'relative-paths',
1252 1255 default='legacy',
1253 1256 )
1254 1257 coreconfigitem('ui', 'remotecmd',
1255 1258 default='hg',
1256 1259 )
1257 1260 coreconfigitem('ui', 'report_untrusted',
1258 1261 default=True,
1259 1262 )
1260 1263 coreconfigitem('ui', 'rollback',
1261 1264 default=True,
1262 1265 )
1263 1266 coreconfigitem('ui', 'signal-safe-lock',
1264 1267 default=True,
1265 1268 )
1266 1269 coreconfigitem('ui', 'slash',
1267 1270 default=False,
1268 1271 )
1269 1272 coreconfigitem('ui', 'ssh',
1270 1273 default='ssh',
1271 1274 )
1272 1275 coreconfigitem('ui', 'ssherrorhint',
1273 1276 default=None,
1274 1277 )
1275 1278 coreconfigitem('ui', 'statuscopies',
1276 1279 default=False,
1277 1280 )
1278 1281 coreconfigitem('ui', 'strict',
1279 1282 default=False,
1280 1283 )
1281 1284 coreconfigitem('ui', 'style',
1282 1285 default='',
1283 1286 )
1284 1287 coreconfigitem('ui', 'supportcontact',
1285 1288 default=None,
1286 1289 )
1287 1290 coreconfigitem('ui', 'textwidth',
1288 1291 default=78,
1289 1292 )
1290 1293 coreconfigitem('ui', 'timeout',
1291 1294 default='600',
1292 1295 )
1293 1296 coreconfigitem('ui', 'timeout.warn',
1294 1297 default=0,
1295 1298 )
1296 1299 coreconfigitem('ui', 'traceback',
1297 1300 default=False,
1298 1301 )
1299 1302 coreconfigitem('ui', 'tweakdefaults',
1300 1303 default=False,
1301 1304 )
1302 1305 coreconfigitem('ui', 'username',
1303 1306 alias=[('ui', 'user')]
1304 1307 )
1305 1308 coreconfigitem('ui', 'verbose',
1306 1309 default=False,
1307 1310 )
1308 1311 coreconfigitem('verify', 'skipflags',
1309 1312 default=None,
1310 1313 )
1311 1314 coreconfigitem('web', 'allowbz2',
1312 1315 default=False,
1313 1316 )
1314 1317 coreconfigitem('web', 'allowgz',
1315 1318 default=False,
1316 1319 )
1317 1320 coreconfigitem('web', 'allow-pull',
1318 1321 alias=[('web', 'allowpull')],
1319 1322 default=True,
1320 1323 )
1321 1324 coreconfigitem('web', 'allow-push',
1322 1325 alias=[('web', 'allow_push')],
1323 1326 default=list,
1324 1327 )
1325 1328 coreconfigitem('web', 'allowzip',
1326 1329 default=False,
1327 1330 )
1328 1331 coreconfigitem('web', 'archivesubrepos',
1329 1332 default=False,
1330 1333 )
1331 1334 coreconfigitem('web', 'cache',
1332 1335 default=True,
1333 1336 )
1334 1337 coreconfigitem('web', 'comparisoncontext',
1335 1338 default=5,
1336 1339 )
1337 1340 coreconfigitem('web', 'contact',
1338 1341 default=None,
1339 1342 )
1340 1343 coreconfigitem('web', 'deny_push',
1341 1344 default=list,
1342 1345 )
1343 1346 coreconfigitem('web', 'guessmime',
1344 1347 default=False,
1345 1348 )
1346 1349 coreconfigitem('web', 'hidden',
1347 1350 default=False,
1348 1351 )
1349 1352 coreconfigitem('web', 'labels',
1350 1353 default=list,
1351 1354 )
1352 1355 coreconfigitem('web', 'logoimg',
1353 1356 default='hglogo.png',
1354 1357 )
1355 1358 coreconfigitem('web', 'logourl',
1356 1359 default='https://mercurial-scm.org/',
1357 1360 )
1358 1361 coreconfigitem('web', 'accesslog',
1359 1362 default='-',
1360 1363 )
1361 1364 coreconfigitem('web', 'address',
1362 1365 default='',
1363 1366 )
1364 1367 coreconfigitem('web', 'allow-archive',
1365 1368 alias=[('web', 'allow_archive')],
1366 1369 default=list,
1367 1370 )
1368 1371 coreconfigitem('web', 'allow_read',
1369 1372 default=list,
1370 1373 )
1371 1374 coreconfigitem('web', 'baseurl',
1372 1375 default=None,
1373 1376 )
1374 1377 coreconfigitem('web', 'cacerts',
1375 1378 default=None,
1376 1379 )
1377 1380 coreconfigitem('web', 'certificate',
1378 1381 default=None,
1379 1382 )
1380 1383 coreconfigitem('web', 'collapse',
1381 1384 default=False,
1382 1385 )
1383 1386 coreconfigitem('web', 'csp',
1384 1387 default=None,
1385 1388 )
1386 1389 coreconfigitem('web', 'deny_read',
1387 1390 default=list,
1388 1391 )
1389 1392 coreconfigitem('web', 'descend',
1390 1393 default=True,
1391 1394 )
1392 1395 coreconfigitem('web', 'description',
1393 1396 default="",
1394 1397 )
1395 1398 coreconfigitem('web', 'encoding',
1396 1399 default=lambda: encoding.encoding,
1397 1400 )
1398 1401 coreconfigitem('web', 'errorlog',
1399 1402 default='-',
1400 1403 )
1401 1404 coreconfigitem('web', 'ipv6',
1402 1405 default=False,
1403 1406 )
1404 1407 coreconfigitem('web', 'maxchanges',
1405 1408 default=10,
1406 1409 )
1407 1410 coreconfigitem('web', 'maxfiles',
1408 1411 default=10,
1409 1412 )
1410 1413 coreconfigitem('web', 'maxshortchanges',
1411 1414 default=60,
1412 1415 )
1413 1416 coreconfigitem('web', 'motd',
1414 1417 default='',
1415 1418 )
1416 1419 coreconfigitem('web', 'name',
1417 1420 default=dynamicdefault,
1418 1421 )
1419 1422 coreconfigitem('web', 'port',
1420 1423 default=8000,
1421 1424 )
1422 1425 coreconfigitem('web', 'prefix',
1423 1426 default='',
1424 1427 )
1425 1428 coreconfigitem('web', 'push_ssl',
1426 1429 default=True,
1427 1430 )
1428 1431 coreconfigitem('web', 'refreshinterval',
1429 1432 default=20,
1430 1433 )
1431 1434 coreconfigitem('web', 'server-header',
1432 1435 default=None,
1433 1436 )
1434 1437 coreconfigitem('web', 'static',
1435 1438 default=None,
1436 1439 )
1437 1440 coreconfigitem('web', 'staticurl',
1438 1441 default=None,
1439 1442 )
1440 1443 coreconfigitem('web', 'stripes',
1441 1444 default=1,
1442 1445 )
1443 1446 coreconfigitem('web', 'style',
1444 1447 default='paper',
1445 1448 )
1446 1449 coreconfigitem('web', 'templates',
1447 1450 default=None,
1448 1451 )
1449 1452 coreconfigitem('web', 'view',
1450 1453 default='served',
1451 1454 )
1452 1455 coreconfigitem('worker', 'backgroundclose',
1453 1456 default=dynamicdefault,
1454 1457 )
1455 1458 # Windows defaults to a limit of 512 open files. A buffer of 128
1456 1459 # should give us enough headway.
1457 1460 coreconfigitem('worker', 'backgroundclosemaxqueue',
1458 1461 default=384,
1459 1462 )
1460 1463 coreconfigitem('worker', 'backgroundcloseminfilecount',
1461 1464 default=2048,
1462 1465 )
1463 1466 coreconfigitem('worker', 'backgroundclosethreadcount',
1464 1467 default=4,
1465 1468 )
1466 1469 coreconfigitem('worker', 'enabled',
1467 1470 default=True,
1468 1471 )
1469 1472 coreconfigitem('worker', 'numcpus',
1470 1473 default=None,
1471 1474 )
1472 1475
1473 1476 # Rebase related configuration moved to core because other extension are doing
1474 1477 # strange things. For example, shelve import the extensions to reuse some bit
1475 1478 # without formally loading it.
1476 1479 coreconfigitem('commands', 'rebase.requiredest',
1477 1480 default=False,
1478 1481 )
1479 1482 coreconfigitem('experimental', 'rebaseskipobsolete',
1480 1483 default=True,
1481 1484 )
1482 1485 coreconfigitem('rebase', 'singletransaction',
1483 1486 default=False,
1484 1487 )
1485 1488 coreconfigitem('rebase', 'experimental.inmemory',
1486 1489 default=False,
1487 1490 )
@@ -1,938 +1,943
1 1 # logcmdutil.py - utility for log-like commands
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 itertools
11 11 import os
12 12 import posixpath
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 nullid,
17 17 wdirid,
18 18 wdirrev,
19 19 )
20 20
21 21 from . import (
22 22 dagop,
23 23 error,
24 24 formatter,
25 25 graphmod,
26 26 match as matchmod,
27 27 mdiff,
28 28 patch,
29 29 pathutil,
30 30 pycompat,
31 31 revset,
32 32 revsetlang,
33 33 scmutil,
34 34 smartset,
35 35 templatekw,
36 36 templater,
37 37 util,
38 38 )
39 39 from .utils import (
40 40 dateutil,
41 41 stringutil,
42 42 )
43 43
44 44 def getlimit(opts):
45 45 """get the log limit according to option -l/--limit"""
46 46 limit = opts.get('limit')
47 47 if limit:
48 48 try:
49 49 limit = int(limit)
50 50 except ValueError:
51 51 raise error.Abort(_('limit must be a positive integer'))
52 52 if limit <= 0:
53 53 raise error.Abort(_('limit must be positive'))
54 54 else:
55 55 limit = None
56 56 return limit
57 57
58 58 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
59 59 changes=None, stat=False, fp=None, graphwidth=0,
60 60 prefix='', root='', listsubrepos=False, hunksfilterfn=None):
61 61 '''show diff or diffstat.'''
62 62 ctx1 = repo[node1]
63 63 ctx2 = repo[node2]
64 64 if root:
65 65 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
66 66 else:
67 67 relroot = ''
68 68 copysourcematch = None
69 69 def compose(f, g):
70 70 return lambda x: f(g(x))
71 71 def pathfn(f):
72 72 return posixpath.join(prefix, f)
73 73 if relroot != '':
74 74 # XXX relative roots currently don't work if the root is within a
75 75 # subrepo
76 76 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
77 77 uirelroot = uipathfn(pathfn(relroot))
78 78 relroot += '/'
79 79 for matchroot in match.files():
80 80 if not matchroot.startswith(relroot):
81 81 ui.warn(_('warning: %s not inside relative root %s\n') %
82 82 (uipathfn(pathfn(matchroot)), uirelroot))
83 83
84 84 relrootmatch = scmutil.match(ctx2, pats=[relroot], default='path')
85 85 match = matchmod.intersectmatchers(match, relrootmatch)
86 86 copysourcematch = relrootmatch
87 87
88 88 checkroot = (repo.ui.configbool('devel', 'all-warnings') or
89 89 repo.ui.configbool('devel', 'check-relroot'))
90 90 def relrootpathfn(f):
91 91 if checkroot and not f.startswith(relroot):
92 92 raise AssertionError(
93 93 "file %s doesn't start with relroot %s" % (f, relroot))
94 94 return f[len(relroot):]
95 95 pathfn = compose(relrootpathfn, pathfn)
96 96
97 97 if stat:
98 98 diffopts = diffopts.copy(context=0, noprefix=False)
99 99 width = 80
100 100 if not ui.plain():
101 101 width = ui.termwidth() - graphwidth
102 102 # If an explicit --root was given, don't respect ui.relative-paths
103 103 if not relroot:
104 104 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
105 105
106 106 chunks = ctx2.diff(ctx1, match, changes, opts=diffopts, pathfn=pathfn,
107 107 copysourcematch=copysourcematch,
108 108 hunksfilterfn=hunksfilterfn)
109 109
110 110 if fp is not None or ui.canwritewithoutlabels():
111 111 out = fp or ui
112 112 if stat:
113 113 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
114 114 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
115 115 out.write(chunk)
116 116 else:
117 117 if stat:
118 118 chunks = patch.diffstatui(util.iterlines(chunks), width=width)
119 119 else:
120 120 chunks = patch.difflabel(lambda chunks, **kwargs: chunks, chunks,
121 121 opts=diffopts)
122 122 if ui.canbatchlabeledwrites():
123 123 def gen():
124 124 for chunk, label in chunks:
125 125 yield ui.label(chunk, label=label)
126 126 for chunk in util.filechunkiter(util.chunkbuffer(gen())):
127 127 ui.write(chunk)
128 128 else:
129 129 for chunk, label in chunks:
130 130 ui.write(chunk, label=label)
131 131
132 132 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
133 133 tempnode2 = node2
134 134 try:
135 135 if node2 is not None:
136 136 tempnode2 = ctx2.substate[subpath][1]
137 137 except KeyError:
138 138 # A subrepo that existed in node1 was deleted between node1 and
139 139 # node2 (inclusive). Thus, ctx2's substate won't contain that
140 140 # subpath. The best we can do is to ignore it.
141 141 tempnode2 = None
142 142 submatch = matchmod.subdirmatcher(subpath, match)
143 143 subprefix = repo.wvfs.reljoin(prefix, subpath)
144 144 if listsubrepos or match.exact(subpath) or any(submatch.files()):
145 145 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
146 146 stat=stat, fp=fp, prefix=subprefix)
147 147
148 148 class changesetdiffer(object):
149 149 """Generate diff of changeset with pre-configured filtering functions"""
150 150
151 151 def _makefilematcher(self, ctx):
152 152 return scmutil.matchall(ctx.repo())
153 153
154 154 def _makehunksfilter(self, ctx):
155 155 return None
156 156
157 157 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False):
158 158 repo = ctx.repo()
159 159 node = ctx.node()
160 160 prev = ctx.p1().node()
161 161 diffordiffstat(ui, repo, diffopts, prev, node,
162 162 match=self._makefilematcher(ctx), stat=stat,
163 163 graphwidth=graphwidth,
164 164 hunksfilterfn=self._makehunksfilter(ctx))
165 165
166 166 def changesetlabels(ctx):
167 167 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
168 168 if ctx.obsolete():
169 169 labels.append('changeset.obsolete')
170 170 if ctx.isunstable():
171 171 labels.append('changeset.unstable')
172 172 for instability in ctx.instabilities():
173 173 labels.append('instability.%s' % instability)
174 174 return ' '.join(labels)
175 175
176 176 class changesetprinter(object):
177 177 '''show changeset information when templating not requested.'''
178 178
179 179 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
180 180 self.ui = ui
181 181 self.repo = repo
182 182 self.buffered = buffered
183 183 self._differ = differ or changesetdiffer()
184 184 self._diffopts = patch.diffallopts(ui, diffopts)
185 185 self._includestat = diffopts and diffopts.get('stat')
186 186 self._includediff = diffopts and diffopts.get('patch')
187 187 self.header = {}
188 188 self.hunk = {}
189 189 self.lastheader = None
190 190 self.footer = None
191 191 self._columns = templatekw.getlogcolumns()
192 192
193 193 def flush(self, ctx):
194 194 rev = ctx.rev()
195 195 if rev in self.header:
196 196 h = self.header[rev]
197 197 if h != self.lastheader:
198 198 self.lastheader = h
199 199 self.ui.write(h)
200 200 del self.header[rev]
201 201 if rev in self.hunk:
202 202 self.ui.write(self.hunk[rev])
203 203 del self.hunk[rev]
204 204
205 205 def close(self):
206 206 if self.footer:
207 207 self.ui.write(self.footer)
208 208
209 209 def show(self, ctx, copies=None, **props):
210 210 props = pycompat.byteskwargs(props)
211 211 if self.buffered:
212 212 self.ui.pushbuffer(labeled=True)
213 213 self._show(ctx, copies, props)
214 214 self.hunk[ctx.rev()] = self.ui.popbuffer()
215 215 else:
216 216 self._show(ctx, copies, props)
217 217
218 218 def _show(self, ctx, copies, props):
219 219 '''show a single changeset or file revision'''
220 220 changenode = ctx.node()
221 221 graphwidth = props.get('graphwidth', 0)
222 222
223 223 if self.ui.quiet:
224 224 self.ui.write("%s\n" % scmutil.formatchangeid(ctx),
225 225 label='log.node')
226 226 return
227 227
228 228 columns = self._columns
229 229 self.ui.write(columns['changeset'] % scmutil.formatchangeid(ctx),
230 230 label=changesetlabels(ctx))
231 231
232 232 # branches are shown first before any other names due to backwards
233 233 # compatibility
234 234 branch = ctx.branch()
235 235 # don't show the default branch name
236 236 if branch != 'default':
237 237 self.ui.write(columns['branch'] % branch, label='log.branch')
238 238
239 239 for nsname, ns in self.repo.names.iteritems():
240 240 # branches has special logic already handled above, so here we just
241 241 # skip it
242 242 if nsname == 'branches':
243 243 continue
244 244 # we will use the templatename as the color name since those two
245 245 # should be the same
246 246 for name in ns.names(self.repo, changenode):
247 247 self.ui.write(ns.logfmt % name,
248 248 label='log.%s' % ns.colorname)
249 249 if self.ui.debugflag:
250 250 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase')
251 251 for pctx in scmutil.meaningfulparents(self.repo, ctx):
252 252 label = 'log.parent changeset.%s' % pctx.phasestr()
253 253 self.ui.write(columns['parent'] % scmutil.formatchangeid(pctx),
254 254 label=label)
255 255
256 256 if self.ui.debugflag:
257 257 mnode = ctx.manifestnode()
258 258 if mnode is None:
259 259 mnode = wdirid
260 260 mrev = wdirrev
261 261 else:
262 262 mrev = self.repo.manifestlog.rev(mnode)
263 263 self.ui.write(columns['manifest']
264 264 % scmutil.formatrevnode(self.ui, mrev, mnode),
265 265 label='ui.debug log.manifest')
266 266 self.ui.write(columns['user'] % ctx.user(), label='log.user')
267 267 self.ui.write(columns['date'] % dateutil.datestr(ctx.date()),
268 268 label='log.date')
269 269
270 270 if ctx.isunstable():
271 271 instabilities = ctx.instabilities()
272 272 self.ui.write(columns['instability'] % ', '.join(instabilities),
273 273 label='log.instability')
274 274
275 275 elif ctx.obsolete():
276 276 self._showobsfate(ctx)
277 277
278 278 self._exthook(ctx)
279 279
280 280 if self.ui.debugflag:
281 281 files = ctx.p1().status(ctx)[:3]
282 282 for key, value in zip(['files', 'files+', 'files-'], files):
283 283 if value:
284 284 self.ui.write(columns[key] % " ".join(value),
285 285 label='ui.debug log.files')
286 286 elif ctx.files() and self.ui.verbose:
287 287 self.ui.write(columns['files'] % " ".join(ctx.files()),
288 288 label='ui.note log.files')
289 289 if copies and self.ui.verbose:
290 290 copies = ['%s (%s)' % c for c in copies]
291 291 self.ui.write(columns['copies'] % ' '.join(copies),
292 292 label='ui.note log.copies')
293 293
294 294 extra = ctx.extra()
295 295 if extra and self.ui.debugflag:
296 296 for key, value in sorted(extra.items()):
297 297 self.ui.write(columns['extra']
298 298 % (key, stringutil.escapestr(value)),
299 299 label='ui.debug log.extra')
300 300
301 301 description = ctx.description().strip()
302 302 if description:
303 303 if self.ui.verbose:
304 304 self.ui.write(_("description:\n"),
305 305 label='ui.note log.description')
306 306 self.ui.write(description,
307 307 label='ui.note log.description')
308 308 self.ui.write("\n\n")
309 309 else:
310 310 self.ui.write(columns['summary'] % description.splitlines()[0],
311 311 label='log.summary')
312 312 self.ui.write("\n")
313 313
314 314 self._showpatch(ctx, graphwidth)
315 315
316 316 def _showobsfate(self, ctx):
317 317 # TODO: do not depend on templater
318 318 tres = formatter.templateresources(self.repo.ui, self.repo)
319 319 t = formatter.maketemplater(self.repo.ui, '{join(obsfate, "\n")}',
320 320 defaults=templatekw.keywords,
321 321 resources=tres)
322 322 obsfate = t.renderdefault({'ctx': ctx}).splitlines()
323 323
324 324 if obsfate:
325 325 for obsfateline in obsfate:
326 326 self.ui.write(self._columns['obsolete'] % obsfateline,
327 327 label='log.obsfate')
328 328
329 329 def _exthook(self, ctx):
330 330 '''empty method used by extension as a hook point
331 331 '''
332 332
333 333 def _showpatch(self, ctx, graphwidth=0):
334 334 if self._includestat:
335 335 self._differ.showdiff(self.ui, ctx, self._diffopts,
336 336 graphwidth, stat=True)
337 337 if self._includestat and self._includediff:
338 338 self.ui.write("\n")
339 339 if self._includediff:
340 340 self._differ.showdiff(self.ui, ctx, self._diffopts,
341 341 graphwidth, stat=False)
342 342 if self._includestat or self._includediff:
343 343 self.ui.write("\n")
344 344
345 345 class changesetformatter(changesetprinter):
346 346 """Format changeset information by generic formatter"""
347 347
348 348 def __init__(self, ui, repo, fm, differ=None, diffopts=None,
349 349 buffered=False):
350 350 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
351 351 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
352 352 self._fm = fm
353 353
354 354 def close(self):
355 355 self._fm.end()
356 356
357 357 def _show(self, ctx, copies, props):
358 358 '''show a single changeset or file revision'''
359 359 fm = self._fm
360 360 fm.startitem()
361 361 fm.context(ctx=ctx)
362 362 fm.data(rev=scmutil.intrev(ctx),
363 363 node=fm.hexfunc(scmutil.binnode(ctx)))
364 364
365 365 if self.ui.quiet:
366 366 return
367 367
368 368 fm.data(branch=ctx.branch(),
369 369 phase=ctx.phasestr(),
370 370 user=ctx.user(),
371 371 date=fm.formatdate(ctx.date()),
372 372 desc=ctx.description(),
373 373 bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'),
374 374 tags=fm.formatlist(ctx.tags(), name='tag'),
375 375 parents=fm.formatlist([fm.hexfunc(c.node())
376 376 for c in ctx.parents()], name='node'))
377 377
378 378 if self.ui.debugflag:
379 379 fm.data(manifest=fm.hexfunc(ctx.manifestnode() or wdirid),
380 380 extra=fm.formatdict(ctx.extra()))
381 381
382 382 files = ctx.p1().status(ctx)
383 383 fm.data(modified=fm.formatlist(files[0], name='file'),
384 384 added=fm.formatlist(files[1], name='file'),
385 385 removed=fm.formatlist(files[2], name='file'))
386 386
387 387 elif self.ui.verbose:
388 388 fm.data(files=fm.formatlist(ctx.files(), name='file'))
389 389 if copies:
390 390 fm.data(copies=fm.formatdict(copies,
391 391 key='name', value='source'))
392 392
393 393 if self._includestat:
394 394 self.ui.pushbuffer()
395 395 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
396 396 fm.data(diffstat=self.ui.popbuffer())
397 397 if self._includediff:
398 398 self.ui.pushbuffer()
399 399 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
400 400 fm.data(diff=self.ui.popbuffer())
401 401
402 402 class changesettemplater(changesetprinter):
403 403 '''format changeset information.
404 404
405 405 Note: there are a variety of convenience functions to build a
406 406 changesettemplater for common cases. See functions such as:
407 407 maketemplater, changesetdisplayer, buildcommittemplate, or other
408 408 functions that use changesest_templater.
409 409 '''
410 410
411 411 # Arguments before "buffered" used to be positional. Consider not
412 412 # adding/removing arguments before "buffered" to not break callers.
413 413 def __init__(self, ui, repo, tmplspec, differ=None, diffopts=None,
414 414 buffered=False):
415 415 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
416 416 # tres is shared with _graphnodeformatter()
417 417 self._tresources = tres = formatter.templateresources(ui, repo)
418 418 self.t = formatter.loadtemplater(ui, tmplspec,
419 419 defaults=templatekw.keywords,
420 420 resources=tres,
421 421 cache=templatekw.defaulttempl)
422 422 self._counter = itertools.count()
423 423
424 424 self._tref = tmplspec.ref
425 425 self._parts = {'header': '', 'footer': '',
426 426 tmplspec.ref: tmplspec.ref,
427 427 'docheader': '', 'docfooter': '',
428 428 'separator': ''}
429 429 if tmplspec.mapfile:
430 430 # find correct templates for current mode, for backward
431 431 # compatibility with 'log -v/-q/--debug' using a mapfile
432 432 tmplmodes = [
433 433 (True, ''),
434 434 (self.ui.verbose, '_verbose'),
435 435 (self.ui.quiet, '_quiet'),
436 436 (self.ui.debugflag, '_debug'),
437 437 ]
438 438 for mode, postfix in tmplmodes:
439 439 for t in self._parts:
440 440 cur = t + postfix
441 441 if mode and cur in self.t:
442 442 self._parts[t] = cur
443 443 else:
444 444 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
445 445 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
446 446 self._parts.update(m)
447 447
448 448 if self._parts['docheader']:
449 449 self.ui.write(self.t.render(self._parts['docheader'], {}))
450 450
451 451 def close(self):
452 452 if self._parts['docfooter']:
453 453 if not self.footer:
454 454 self.footer = ""
455 455 self.footer += self.t.render(self._parts['docfooter'], {})
456 456 return super(changesettemplater, self).close()
457 457
458 458 def _show(self, ctx, copies, props):
459 459 '''show a single changeset or file revision'''
460 460 props = props.copy()
461 461 props['ctx'] = ctx
462 462 props['index'] = index = next(self._counter)
463 463 props['revcache'] = {'copies': copies}
464 464 graphwidth = props.get('graphwidth', 0)
465 465
466 466 # write separator, which wouldn't work well with the header part below
467 467 # since there's inherently a conflict between header (across items) and
468 468 # separator (per item)
469 469 if self._parts['separator'] and index > 0:
470 470 self.ui.write(self.t.render(self._parts['separator'], {}))
471 471
472 472 # write header
473 473 if self._parts['header']:
474 474 h = self.t.render(self._parts['header'], props)
475 475 if self.buffered:
476 476 self.header[ctx.rev()] = h
477 477 else:
478 478 if self.lastheader != h:
479 479 self.lastheader = h
480 480 self.ui.write(h)
481 481
482 482 # write changeset metadata, then patch if requested
483 483 key = self._parts[self._tref]
484 484 self.ui.write(self.t.render(key, props))
485 485 self._showpatch(ctx, graphwidth)
486 486
487 487 if self._parts['footer']:
488 488 if not self.footer:
489 489 self.footer = self.t.render(self._parts['footer'], props)
490 490
491 491 def templatespec(tmpl, mapfile):
492 492 if pycompat.ispy3:
493 493 assert not isinstance(tmpl, str), 'tmpl must not be a str'
494 494 if mapfile:
495 495 return formatter.templatespec('changeset', tmpl, mapfile)
496 496 else:
497 497 return formatter.templatespec('', tmpl, None)
498 498
499 499 def _lookuptemplate(ui, tmpl, style):
500 500 """Find the template matching the given template spec or style
501 501
502 502 See formatter.lookuptemplate() for details.
503 503 """
504 504
505 505 # ui settings
506 506 if not tmpl and not style: # template are stronger than style
507 507 tmpl = ui.config('ui', 'logtemplate')
508 508 if tmpl:
509 509 return templatespec(templater.unquotestring(tmpl), None)
510 510 else:
511 511 style = util.expandpath(ui.config('ui', 'style'))
512 512
513 513 if not tmpl and style:
514 514 mapfile = style
515 515 if not os.path.split(mapfile)[0]:
516 516 mapname = (templater.templatepath('map-cmdline.' + mapfile)
517 517 or templater.templatepath(mapfile))
518 518 if mapname:
519 519 mapfile = mapname
520 520 return templatespec(None, mapfile)
521 521
522 522 if not tmpl:
523 523 return templatespec(None, None)
524 524
525 525 return formatter.lookuptemplate(ui, 'changeset', tmpl)
526 526
527 527 def maketemplater(ui, repo, tmpl, buffered=False):
528 528 """Create a changesettemplater from a literal template 'tmpl'
529 529 byte-string."""
530 530 spec = templatespec(tmpl, None)
531 531 return changesettemplater(ui, repo, spec, buffered=buffered)
532 532
533 533 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
534 534 """show one changeset using template or regular display.
535 535
536 536 Display format will be the first non-empty hit of:
537 537 1. option 'template'
538 538 2. option 'style'
539 539 3. [ui] setting 'logtemplate'
540 540 4. [ui] setting 'style'
541 541 If all of these values are either the unset or the empty string,
542 542 regular display via changesetprinter() is done.
543 543 """
544 544 postargs = (differ, opts, buffered)
545 545 if opts.get('template') in {'cbor', 'json'}:
546 546 fm = ui.formatter('log', opts)
547 547 return changesetformatter(ui, repo, fm, *postargs)
548 548
549 549 spec = _lookuptemplate(ui, opts.get('template'), opts.get('style'))
550 550
551 551 if not spec.ref and not spec.tmpl and not spec.mapfile:
552 552 return changesetprinter(ui, repo, *postargs)
553 553
554 554 return changesettemplater(ui, repo, spec, *postargs)
555 555
556 556 def _makematcher(repo, revs, pats, opts):
557 557 """Build matcher and expanded patterns from log options
558 558
559 559 If --follow, revs are the revisions to follow from.
560 560
561 561 Returns (match, pats, slowpath) where
562 562 - match: a matcher built from the given pats and -I/-X opts
563 563 - pats: patterns used (globs are expanded on Windows)
564 564 - slowpath: True if patterns aren't as simple as scanning filelogs
565 565 """
566 566 # pats/include/exclude are passed to match.match() directly in
567 567 # _matchfiles() revset but walkchangerevs() builds its matcher with
568 568 # scmutil.match(). The difference is input pats are globbed on
569 569 # platforms without shell expansion (windows).
570 570 wctx = repo[None]
571 571 match, pats = scmutil.matchandpats(wctx, pats, opts)
572 572 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
573 573 if not slowpath:
574 574 follow = opts.get('follow') or opts.get('follow_first')
575 575 startctxs = []
576 576 if follow and opts.get('rev'):
577 577 startctxs = [repo[r] for r in revs]
578 578 for f in match.files():
579 579 if follow and startctxs:
580 580 # No idea if the path was a directory at that revision, so
581 581 # take the slow path.
582 582 if any(f not in c for c in startctxs):
583 583 slowpath = True
584 584 continue
585 585 elif follow and f not in wctx:
586 586 # If the file exists, it may be a directory, so let it
587 587 # take the slow path.
588 588 if os.path.exists(repo.wjoin(f)):
589 589 slowpath = True
590 590 continue
591 591 else:
592 592 raise error.Abort(_('cannot follow file not in parent '
593 593 'revision: "%s"') % f)
594 594 filelog = repo.file(f)
595 595 if not filelog:
596 596 # A zero count may be a directory or deleted file, so
597 597 # try to find matching entries on the slow path.
598 598 if follow:
599 599 raise error.Abort(
600 600 _('cannot follow nonexistent file: "%s"') % f)
601 601 slowpath = True
602 602
603 603 # We decided to fall back to the slowpath because at least one
604 604 # of the paths was not a file. Check to see if at least one of them
605 605 # existed in history - in that case, we'll continue down the
606 606 # slowpath; otherwise, we can turn off the slowpath
607 607 if slowpath:
608 608 for path in match.files():
609 609 if path == '.' or path in repo.store:
610 610 break
611 611 else:
612 612 slowpath = False
613 613
614 614 return match, pats, slowpath
615 615
616 616 def _fileancestors(repo, revs, match, followfirst):
617 617 fctxs = []
618 618 for r in revs:
619 619 ctx = repo[r]
620 620 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
621 621
622 622 # When displaying a revision with --patch --follow FILE, we have
623 623 # to know which file of the revision must be diffed. With
624 624 # --follow, we want the names of the ancestors of FILE in the
625 625 # revision, stored in "fcache". "fcache" is populated as a side effect
626 626 # of the graph traversal.
627 627 fcache = {}
628 628 def filematcher(ctx):
629 629 return scmutil.matchfiles(repo, fcache.get(ctx.rev(), []))
630 630
631 631 def revgen():
632 632 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
633 633 fcache[rev] = [c.path() for c in cs]
634 634 yield rev
635 635 return smartset.generatorset(revgen(), iterasc=False), filematcher
636 636
637 637 def _makenofollowfilematcher(repo, pats, opts):
638 638 '''hook for extensions to override the filematcher for non-follow cases'''
639 639 return None
640 640
641 641 _opt2logrevset = {
642 642 'no_merges': ('not merge()', None),
643 643 'only_merges': ('merge()', None),
644 644 '_matchfiles': (None, '_matchfiles(%ps)'),
645 645 'date': ('date(%s)', None),
646 646 'branch': ('branch(%s)', '%lr'),
647 647 '_patslog': ('filelog(%s)', '%lr'),
648 648 'keyword': ('keyword(%s)', '%lr'),
649 649 'prune': ('ancestors(%s)', 'not %lr'),
650 650 'user': ('user(%s)', '%lr'),
651 651 }
652 652
653 653 def _makerevset(repo, match, pats, slowpath, opts):
654 654 """Return a revset string built from log options and file patterns"""
655 655 opts = dict(opts)
656 656 # follow or not follow?
657 657 follow = opts.get('follow') or opts.get('follow_first')
658 658
659 659 # branch and only_branch are really aliases and must be handled at
660 660 # the same time
661 661 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
662 662 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
663 663
664 664 if slowpath:
665 665 # See walkchangerevs() slow path.
666 666 #
667 667 # pats/include/exclude cannot be represented as separate
668 668 # revset expressions as their filtering logic applies at file
669 669 # level. For instance "-I a -X b" matches a revision touching
670 670 # "a" and "b" while "file(a) and not file(b)" does
671 671 # not. Besides, filesets are evaluated against the working
672 672 # directory.
673 673 matchargs = ['r:', 'd:relpath']
674 674 for p in pats:
675 675 matchargs.append('p:' + p)
676 676 for p in opts.get('include', []):
677 677 matchargs.append('i:' + p)
678 678 for p in opts.get('exclude', []):
679 679 matchargs.append('x:' + p)
680 680 opts['_matchfiles'] = matchargs
681 681 elif not follow:
682 682 opts['_patslog'] = list(pats)
683 683
684 684 expr = []
685 685 for op, val in sorted(opts.iteritems()):
686 686 if not val:
687 687 continue
688 688 if op not in _opt2logrevset:
689 689 continue
690 690 revop, listop = _opt2logrevset[op]
691 691 if revop and '%' not in revop:
692 692 expr.append(revop)
693 693 elif not listop:
694 694 expr.append(revsetlang.formatspec(revop, val))
695 695 else:
696 696 if revop:
697 697 val = [revsetlang.formatspec(revop, v) for v in val]
698 698 expr.append(revsetlang.formatspec(listop, val))
699 699
700 700 if expr:
701 701 expr = '(' + ' and '.join(expr) + ')'
702 702 else:
703 703 expr = None
704 704 return expr
705 705
706 706 def _initialrevs(repo, opts):
707 707 """Return the initial set of revisions to be filtered or followed"""
708 708 follow = opts.get('follow') or opts.get('follow_first')
709 709 if opts.get('rev'):
710 710 revs = scmutil.revrange(repo, opts['rev'])
711 711 elif follow and repo.dirstate.p1() == nullid:
712 712 revs = smartset.baseset()
713 713 elif follow:
714 714 revs = repo.revs('.')
715 715 else:
716 716 revs = smartset.spanset(repo)
717 717 revs.reverse()
718 718 return revs
719 719
720 720 def getrevs(repo, pats, opts):
721 721 """Return (revs, differ) where revs is a smartset
722 722
723 723 differ is a changesetdiffer with pre-configured file matcher.
724 724 """
725 725 follow = opts.get('follow') or opts.get('follow_first')
726 726 followfirst = opts.get('follow_first')
727 727 limit = getlimit(opts)
728 728 revs = _initialrevs(repo, opts)
729 729 if not revs:
730 730 return smartset.baseset(), None
731 731 match, pats, slowpath = _makematcher(repo, revs, pats, opts)
732 732 filematcher = None
733 733 if follow:
734 734 if slowpath or match.always():
735 735 revs = dagop.revancestors(repo, revs, followfirst=followfirst)
736 736 else:
737 737 revs, filematcher = _fileancestors(repo, revs, match, followfirst)
738 738 revs.reverse()
739 739 if filematcher is None:
740 740 filematcher = _makenofollowfilematcher(repo, pats, opts)
741 741 if filematcher is None:
742 742 def filematcher(ctx):
743 743 return match
744 744
745 745 expr = _makerevset(repo, match, pats, slowpath, opts)
746 746 if opts.get('graph'):
747 747 # User-specified revs might be unsorted, but don't sort before
748 748 # _makerevset because it might depend on the order of revs
749 if not (revs.isdescending() or revs.istopo()):
749 if repo.ui.configbool('experimental', 'log.topo'):
750 if not revs.istopo():
751 revs = dagop.toposort(revs, repo.changelog.parentrevs)
752 # TODO: try to iterate the set lazily
753 revs = revset.baseset(list(revs))
754 elif not (revs.isdescending() or revs.istopo()):
750 755 revs.sort(reverse=True)
751 756 if expr:
752 757 matcher = revset.match(None, expr)
753 758 revs = matcher(repo, revs)
754 759 if limit is not None:
755 760 revs = revs.slice(0, limit)
756 761
757 762 differ = changesetdiffer()
758 763 differ._makefilematcher = filematcher
759 764 return revs, differ
760 765
761 766 def _parselinerangeopt(repo, opts):
762 767 """Parse --line-range log option and return a list of tuples (filename,
763 768 (fromline, toline)).
764 769 """
765 770 linerangebyfname = []
766 771 for pat in opts.get('line_range', []):
767 772 try:
768 773 pat, linerange = pat.rsplit(',', 1)
769 774 except ValueError:
770 775 raise error.Abort(_('malformatted line-range pattern %s') % pat)
771 776 try:
772 777 fromline, toline = map(int, linerange.split(':'))
773 778 except ValueError:
774 779 raise error.Abort(_("invalid line range for %s") % pat)
775 780 msg = _("line range pattern '%s' must match exactly one file") % pat
776 781 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
777 782 linerangebyfname.append(
778 783 (fname, util.processlinerange(fromline, toline)))
779 784 return linerangebyfname
780 785
781 786 def getlinerangerevs(repo, userrevs, opts):
782 787 """Return (revs, differ).
783 788
784 789 "revs" are revisions obtained by processing "line-range" log options and
785 790 walking block ancestors of each specified file/line-range.
786 791
787 792 "differ" is a changesetdiffer with pre-configured file matcher and hunks
788 793 filter.
789 794 """
790 795 wctx = repo[None]
791 796
792 797 # Two-levels map of "rev -> file ctx -> [line range]".
793 798 linerangesbyrev = {}
794 799 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
795 800 if fname not in wctx:
796 801 raise error.Abort(_('cannot follow file not in parent '
797 802 'revision: "%s"') % fname)
798 803 fctx = wctx.filectx(fname)
799 804 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
800 805 rev = fctx.introrev()
801 806 if rev not in userrevs:
802 807 continue
803 808 linerangesbyrev.setdefault(
804 809 rev, {}).setdefault(
805 810 fctx.path(), []).append(linerange)
806 811
807 812 def nofilterhunksfn(fctx, hunks):
808 813 return hunks
809 814
810 815 def hunksfilter(ctx):
811 816 fctxlineranges = linerangesbyrev.get(ctx.rev())
812 817 if fctxlineranges is None:
813 818 return nofilterhunksfn
814 819
815 820 def filterfn(fctx, hunks):
816 821 lineranges = fctxlineranges.get(fctx.path())
817 822 if lineranges is not None:
818 823 for hr, lines in hunks:
819 824 if hr is None: # binary
820 825 yield hr, lines
821 826 continue
822 827 if any(mdiff.hunkinrange(hr[2:], lr)
823 828 for lr in lineranges):
824 829 yield hr, lines
825 830 else:
826 831 for hunk in hunks:
827 832 yield hunk
828 833
829 834 return filterfn
830 835
831 836 def filematcher(ctx):
832 837 files = list(linerangesbyrev.get(ctx.rev(), []))
833 838 return scmutil.matchfiles(repo, files)
834 839
835 840 revs = sorted(linerangesbyrev, reverse=True)
836 841
837 842 differ = changesetdiffer()
838 843 differ._makefilematcher = filematcher
839 844 differ._makehunksfilter = hunksfilter
840 845 return revs, differ
841 846
842 847 def _graphnodeformatter(ui, displayer):
843 848 spec = ui.config('ui', 'graphnodetemplate')
844 849 if not spec:
845 850 return templatekw.getgraphnode # fast path for "{graphnode}"
846 851
847 852 spec = templater.unquotestring(spec)
848 853 if isinstance(displayer, changesettemplater):
849 854 # reuse cache of slow templates
850 855 tres = displayer._tresources
851 856 else:
852 857 tres = formatter.templateresources(ui)
853 858 templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords,
854 859 resources=tres)
855 860 def formatnode(repo, ctx):
856 861 props = {'ctx': ctx, 'repo': repo}
857 862 return templ.renderdefault(props)
858 863 return formatnode
859 864
860 865 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None, props=None):
861 866 props = props or {}
862 867 formatnode = _graphnodeformatter(ui, displayer)
863 868 state = graphmod.asciistate()
864 869 styles = state['styles']
865 870
866 871 # only set graph styling if HGPLAIN is not set.
867 872 if ui.plain('graph'):
868 873 # set all edge styles to |, the default pre-3.8 behaviour
869 874 styles.update(dict.fromkeys(styles, '|'))
870 875 else:
871 876 edgetypes = {
872 877 'parent': graphmod.PARENT,
873 878 'grandparent': graphmod.GRANDPARENT,
874 879 'missing': graphmod.MISSINGPARENT
875 880 }
876 881 for name, key in edgetypes.items():
877 882 # experimental config: experimental.graphstyle.*
878 883 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
879 884 styles[key])
880 885 if not styles[key]:
881 886 styles[key] = None
882 887
883 888 # experimental config: experimental.graphshorten
884 889 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
885 890
886 891 for rev, type, ctx, parents in dag:
887 892 char = formatnode(repo, ctx)
888 893 copies = None
889 894 if getrenamed and ctx.rev():
890 895 copies = []
891 896 for fn in ctx.files():
892 897 rename = getrenamed(fn, ctx.rev())
893 898 if rename:
894 899 copies.append((fn, rename))
895 900 edges = edgefn(type, char, state, rev, parents)
896 901 firstedge = next(edges)
897 902 width = firstedge[2]
898 903 displayer.show(ctx, copies=copies,
899 904 graphwidth=width, **pycompat.strkwargs(props))
900 905 lines = displayer.hunk.pop(rev).split('\n')
901 906 if not lines[-1]:
902 907 del lines[-1]
903 908 displayer.flush(ctx)
904 909 for type, char, width, coldata in itertools.chain([firstedge], edges):
905 910 graphmod.ascii(ui, state, type, char, lines, coldata)
906 911 lines = []
907 912 displayer.close()
908 913
909 914 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
910 915 revdag = graphmod.dagwalker(repo, revs)
911 916 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
912 917
913 918 def displayrevs(ui, repo, revs, displayer, getrenamed):
914 919 for rev in revs:
915 920 ctx = repo[rev]
916 921 copies = None
917 922 if getrenamed is not None and rev:
918 923 copies = []
919 924 for fn in ctx.files():
920 925 rename = getrenamed(fn, rev)
921 926 if rename:
922 927 copies.append((fn, rename))
923 928 displayer.show(ctx, copies=copies)
924 929 displayer.flush(ctx)
925 930 displayer.close()
926 931
927 932 def checkunsupportedgraphflags(pats, opts):
928 933 for op in ["newest_first"]:
929 934 if op in opts and opts[op]:
930 935 raise error.Abort(_("-G/--graph option is incompatible with --%s")
931 936 % op.replace("_", "-"))
932 937
933 938 def graphrevs(repo, nodes, opts):
934 939 limit = getlimit(opts)
935 940 nodes.reverse()
936 941 if limit is not None:
937 942 nodes = nodes[:limit]
938 943 return graphmod.nodes(repo, nodes)
@@ -1,14 +1,24
1 1 == New Features ==
2 2
3 3 * New config `commands.commit.post-status` shows status after successful
4 4 commit.
5 5
6
7 == New Experimental Features ==
8
9 * New config `experimental.log.topo` makes `hg log -G` use
10 topological sorting. This is especially useful for aliases since it
11 lets the alias accept an `-r` option while still using topological
12 sorting with or without the `-r` (unlike if you use the `sort(...,
13 topo)` revset).
14
15
6 16 == Bug Fixes ==
7 17
8 18
9 19 == Backwards Compatibility Changes ==
10 20
11 21
12 22 == Internal API Changes ==
13 23
14 24 * Matchers are no longer iterable. Use `match.files()` instead.
@@ -1,116 +1,154
1 1 This test file aims at test topological iteration and the various configuration it can has.
2 2
3 3 $ cat >> $HGRCPATH << EOF
4 4 > [ui]
5 5 > logtemplate={rev}\n
6 6 > EOF
7 7
8 8 On this simple example, all topological branch are displayed in turn until we
9 9 can finally display 0. this implies skipping from 8 to 3 and coming back to 7
10 10 later.
11 11
12 12 $ hg init test01
13 13 $ cd test01
14 14 $ hg unbundle $TESTDIR/bundles/remote.hg
15 15 adding changesets
16 16 adding manifests
17 17 adding file changes
18 18 added 9 changesets with 7 changes to 4 files (+1 heads)
19 19 new changesets bfaf4b5cbf01:916f1afdef90 (9 drafts)
20 20 (run 'hg heads' to see heads, 'hg merge' to merge)
21 21
22 22 $ hg log -G
23 23 o 8
24 24 |
25 25 | o 7
26 26 | |
27 27 | o 6
28 28 | |
29 29 | o 5
30 30 | |
31 31 | o 4
32 32 | |
33 33 o | 3
34 34 | |
35 35 o | 2
36 36 | |
37 37 o | 1
38 38 |/
39 39 o 0
40 40
41 41
42 42 (display all nodes)
43 43
44 44 $ hg log -G -r 'sort(all(), topo)'
45 45 o 8
46 46 |
47 47 o 3
48 48 |
49 49 o 2
50 50 |
51 51 o 1
52 52 |
53 53 | o 7
54 54 | |
55 55 | o 6
56 56 | |
57 57 | o 5
58 58 | |
59 59 | o 4
60 60 |/
61 61 o 0
62 62
63 63
64 64 (display nodes filtered by log options)
65 65
66 66 $ hg log -G -r 'sort(all(), topo)' -k '.3'
67 67 o 8
68 68 |
69 69 o 3
70 70 |
71 71 ~
72 72 o 7
73 73 |
74 74 o 6
75 75 |
76 76 ~
77 77
78 78 (revset skipping nodes)
79 79
80 80 $ hg log -G --rev 'sort(not (2+6), topo)'
81 81 o 8
82 82 |
83 83 o 3
84 84 :
85 85 o 1
86 86 |
87 87 | o 7
88 88 | :
89 89 | o 5
90 90 | |
91 91 | o 4
92 92 |/
93 93 o 0
94 94
95 95
96 96 (begin) from the other branch
97 97
98 98 $ hg log -G -r 'sort(all(), topo, topo.firstbranch=5)'
99 99 o 7
100 100 |
101 101 o 6
102 102 |
103 103 o 5
104 104 |
105 105 o 4
106 106 |
107 107 | o 8
108 108 | |
109 109 | o 3
110 110 | |
111 111 | o 2
112 112 | |
113 113 | o 1
114 114 |/
115 115 o 0
116 116
117
118 Topological sort can be turned on via config
119
120 $ cat >> $HGRCPATH << EOF
121 > [experimental]
122 > log.topo=true
123 > EOF
124
125 $ hg log -G
126 o 8
127 |
128 o 3
129 |
130 o 2
131 |
132 o 1
133 |
134 | o 7
135 | |
136 | o 6
137 | |
138 | o 5
139 | |
140 | o 4
141 |/
142 o 0
143
144 Does not affect non-graph log
145 $ hg log -T '{rev}\n'
146 8
147 7
148 6
149 5
150 4
151 3
152 2
153 1
154 0
General Comments 0
You need to be logged in to leave comments. Login now