##// END OF EJS Templates
merge with stable
Augie Fackler -
r40972:e06719b7 merge default
parent child Browse files
Show More
@@ -1,1460 +1,1463 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import functools
11 11 import re
12 12
13 13 from . import (
14 14 encoding,
15 15 error,
16 16 )
17 17
18 18 def loadconfigtable(ui, extname, configtable):
19 19 """update config item known to the ui with the extension ones"""
20 20 for section, items in sorted(configtable.items()):
21 21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 22 knownkeys = set(knownitems)
23 23 newkeys = set(items)
24 24 for key in sorted(knownkeys & newkeys):
25 25 msg = "extension '%s' overwrite config item '%s.%s'"
26 26 msg %= (extname, section, key)
27 27 ui.develwarn(msg, config='warn-config')
28 28
29 29 knownitems.update(items)
30 30
31 31 class configitem(object):
32 32 """represent a known config item
33 33
34 34 :section: the official config section where to find this item,
35 35 :name: the official name within the section,
36 36 :default: default value for this item,
37 37 :alias: optional list of tuples as alternatives,
38 38 :generic: this is a generic definition, match name using regular expression.
39 39 """
40 40
41 41 def __init__(self, section, name, default=None, alias=(),
42 42 generic=False, priority=0):
43 43 self.section = section
44 44 self.name = name
45 45 self.default = default
46 46 self.alias = list(alias)
47 47 self.generic = generic
48 48 self.priority = priority
49 49 self._re = None
50 50 if generic:
51 51 self._re = re.compile(self.name)
52 52
53 53 class itemregister(dict):
54 54 """A specialized dictionary that can handle wild-card selection"""
55 55
56 56 def __init__(self):
57 57 super(itemregister, self).__init__()
58 58 self._generics = set()
59 59
60 60 def update(self, other):
61 61 super(itemregister, self).update(other)
62 62 self._generics.update(other._generics)
63 63
64 64 def __setitem__(self, key, item):
65 65 super(itemregister, self).__setitem__(key, item)
66 66 if item.generic:
67 67 self._generics.add(item)
68 68
69 69 def get(self, key):
70 70 baseitem = super(itemregister, self).get(key)
71 71 if baseitem is not None and not baseitem.generic:
72 72 return baseitem
73 73
74 74 # search for a matching generic item
75 75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
76 76 for item in generics:
77 77 # we use 'match' instead of 'search' to make the matching simpler
78 78 # for people unfamiliar with regular expression. Having the match
79 79 # rooted to the start of the string will produce less surprising
80 80 # result for user writing simple regex for sub-attribute.
81 81 #
82 82 # For example using "color\..*" match produces an unsurprising
83 83 # result, while using search could suddenly match apparently
84 84 # unrelated configuration that happens to contains "color."
85 85 # anywhere. This is a tradeoff where we favor requiring ".*" on
86 86 # some match to avoid the need to prefix most pattern with "^".
87 87 # The "^" seems more error prone.
88 88 if item._re.match(key):
89 89 return item
90 90
91 91 return None
92 92
93 93 coreitems = {}
94 94
95 95 def _register(configtable, *args, **kwargs):
96 96 item = configitem(*args, **kwargs)
97 97 section = configtable.setdefault(item.section, itemregister())
98 98 if item.name in section:
99 99 msg = "duplicated config item registration for '%s.%s'"
100 100 raise error.ProgrammingError(msg % (item.section, item.name))
101 101 section[item.name] = item
102 102
103 103 # special value for case where the default is derived from other values
104 104 dynamicdefault = object()
105 105
106 106 # Registering actual config items
107 107
108 108 def getitemregister(configtable):
109 109 f = functools.partial(_register, configtable)
110 110 # export pseudo enum as configitem.*
111 111 f.dynamicdefault = dynamicdefault
112 112 return f
113 113
114 114 coreconfigitem = getitemregister(coreitems)
115 115
116 116 coreconfigitem('alias', '.*',
117 117 default=dynamicdefault,
118 118 generic=True,
119 119 )
120 120 coreconfigitem('annotate', 'nodates',
121 121 default=False,
122 122 )
123 123 coreconfigitem('annotate', 'showfunc',
124 124 default=False,
125 125 )
126 126 coreconfigitem('annotate', 'unified',
127 127 default=None,
128 128 )
129 129 coreconfigitem('annotate', 'git',
130 130 default=False,
131 131 )
132 132 coreconfigitem('annotate', 'ignorews',
133 133 default=False,
134 134 )
135 135 coreconfigitem('annotate', 'ignorewsamount',
136 136 default=False,
137 137 )
138 138 coreconfigitem('annotate', 'ignoreblanklines',
139 139 default=False,
140 140 )
141 141 coreconfigitem('annotate', 'ignorewseol',
142 142 default=False,
143 143 )
144 144 coreconfigitem('annotate', 'nobinary',
145 145 default=False,
146 146 )
147 147 coreconfigitem('annotate', 'noprefix',
148 148 default=False,
149 149 )
150 150 coreconfigitem('annotate', 'word-diff',
151 151 default=False,
152 152 )
153 153 coreconfigitem('auth', 'cookiefile',
154 154 default=None,
155 155 )
156 156 # bookmarks.pushing: internal hack for discovery
157 157 coreconfigitem('bookmarks', 'pushing',
158 158 default=list,
159 159 )
160 160 # bundle.mainreporoot: internal hack for bundlerepo
161 161 coreconfigitem('bundle', 'mainreporoot',
162 162 default='',
163 163 )
164 164 coreconfigitem('censor', 'policy',
165 165 default='abort',
166 166 )
167 167 coreconfigitem('chgserver', 'idletimeout',
168 168 default=3600,
169 169 )
170 170 coreconfigitem('chgserver', 'skiphash',
171 171 default=False,
172 172 )
173 173 coreconfigitem('cmdserver', 'log',
174 174 default=None,
175 175 )
176 176 coreconfigitem('cmdserver', 'max-log-files',
177 177 default=7,
178 178 )
179 179 coreconfigitem('cmdserver', 'max-log-size',
180 180 default='1 MB',
181 181 )
182 182 coreconfigitem('cmdserver', 'message-encodings',
183 183 default=list,
184 184 )
185 185 coreconfigitem('cmdserver', 'track-log',
186 186 default=lambda: ['chgserver', 'cmdserver'],
187 187 )
188 188 coreconfigitem('color', '.*',
189 189 default=None,
190 190 generic=True,
191 191 )
192 192 coreconfigitem('color', 'mode',
193 193 default='auto',
194 194 )
195 195 coreconfigitem('color', 'pagermode',
196 196 default=dynamicdefault,
197 197 )
198 198 coreconfigitem('commands', 'grep.all-files',
199 199 default=False,
200 200 )
201 201 coreconfigitem('commands', 'resolve.confirm',
202 202 default=False,
203 203 )
204 204 coreconfigitem('commands', 'resolve.explicit-re-merge',
205 205 default=False,
206 206 )
207 207 coreconfigitem('commands', 'resolve.mark-check',
208 208 default='none',
209 209 )
210 210 coreconfigitem('commands', 'show.aliasprefix',
211 211 default=list,
212 212 )
213 213 coreconfigitem('commands', 'status.relative',
214 214 default=False,
215 215 )
216 216 coreconfigitem('commands', 'status.skipstates',
217 217 default=[],
218 218 )
219 219 coreconfigitem('commands', 'status.terse',
220 220 default='',
221 221 )
222 222 coreconfigitem('commands', 'status.verbose',
223 223 default=False,
224 224 )
225 225 coreconfigitem('commands', 'update.check',
226 226 default=None,
227 227 )
228 228 coreconfigitem('commands', 'update.requiredest',
229 229 default=False,
230 230 )
231 231 coreconfigitem('committemplate', '.*',
232 232 default=None,
233 233 generic=True,
234 234 )
235 235 coreconfigitem('convert', 'bzr.saverev',
236 236 default=True,
237 237 )
238 238 coreconfigitem('convert', 'cvsps.cache',
239 239 default=True,
240 240 )
241 241 coreconfigitem('convert', 'cvsps.fuzz',
242 242 default=60,
243 243 )
244 244 coreconfigitem('convert', 'cvsps.logencoding',
245 245 default=None,
246 246 )
247 247 coreconfigitem('convert', 'cvsps.mergefrom',
248 248 default=None,
249 249 )
250 250 coreconfigitem('convert', 'cvsps.mergeto',
251 251 default=None,
252 252 )
253 253 coreconfigitem('convert', 'git.committeractions',
254 254 default=lambda: ['messagedifferent'],
255 255 )
256 256 coreconfigitem('convert', 'git.extrakeys',
257 257 default=list,
258 258 )
259 259 coreconfigitem('convert', 'git.findcopiesharder',
260 260 default=False,
261 261 )
262 262 coreconfigitem('convert', 'git.remoteprefix',
263 263 default='remote',
264 264 )
265 265 coreconfigitem('convert', 'git.renamelimit',
266 266 default=400,
267 267 )
268 268 coreconfigitem('convert', 'git.saverev',
269 269 default=True,
270 270 )
271 271 coreconfigitem('convert', 'git.similarity',
272 272 default=50,
273 273 )
274 274 coreconfigitem('convert', 'git.skipsubmodules',
275 275 default=False,
276 276 )
277 277 coreconfigitem('convert', 'hg.clonebranches',
278 278 default=False,
279 279 )
280 280 coreconfigitem('convert', 'hg.ignoreerrors',
281 281 default=False,
282 282 )
283 283 coreconfigitem('convert', 'hg.revs',
284 284 default=None,
285 285 )
286 286 coreconfigitem('convert', 'hg.saverev',
287 287 default=False,
288 288 )
289 289 coreconfigitem('convert', 'hg.sourcename',
290 290 default=None,
291 291 )
292 292 coreconfigitem('convert', 'hg.startrev',
293 293 default=None,
294 294 )
295 295 coreconfigitem('convert', 'hg.tagsbranch',
296 296 default='default',
297 297 )
298 298 coreconfigitem('convert', 'hg.usebranchnames',
299 299 default=True,
300 300 )
301 301 coreconfigitem('convert', 'ignoreancestorcheck',
302 302 default=False,
303 303 )
304 304 coreconfigitem('convert', 'localtimezone',
305 305 default=False,
306 306 )
307 307 coreconfigitem('convert', 'p4.encoding',
308 308 default=dynamicdefault,
309 309 )
310 310 coreconfigitem('convert', 'p4.startrev',
311 311 default=0,
312 312 )
313 313 coreconfigitem('convert', 'skiptags',
314 314 default=False,
315 315 )
316 316 coreconfigitem('convert', 'svn.debugsvnlog',
317 317 default=True,
318 318 )
319 319 coreconfigitem('convert', 'svn.trunk',
320 320 default=None,
321 321 )
322 322 coreconfigitem('convert', 'svn.tags',
323 323 default=None,
324 324 )
325 325 coreconfigitem('convert', 'svn.branches',
326 326 default=None,
327 327 )
328 328 coreconfigitem('convert', 'svn.startrev',
329 329 default=0,
330 330 )
331 331 coreconfigitem('debug', 'dirstate.delaywrite',
332 332 default=0,
333 333 )
334 334 coreconfigitem('defaults', '.*',
335 335 default=None,
336 336 generic=True,
337 337 )
338 338 coreconfigitem('devel', 'all-warnings',
339 339 default=False,
340 340 )
341 341 coreconfigitem('devel', 'bundle2.debug',
342 342 default=False,
343 343 )
344 344 coreconfigitem('devel', 'bundle.delta',
345 345 default='',
346 346 )
347 347 coreconfigitem('devel', 'cache-vfs',
348 348 default=None,
349 349 )
350 350 coreconfigitem('devel', 'check-locks',
351 351 default=False,
352 352 )
353 353 coreconfigitem('devel', 'check-relroot',
354 354 default=False,
355 355 )
356 356 coreconfigitem('devel', 'default-date',
357 357 default=None,
358 358 )
359 359 coreconfigitem('devel', 'deprec-warn',
360 360 default=False,
361 361 )
362 362 coreconfigitem('devel', 'disableloaddefaultcerts',
363 363 default=False,
364 364 )
365 365 coreconfigitem('devel', 'warn-empty-changegroup',
366 366 default=False,
367 367 )
368 368 coreconfigitem('devel', 'legacy.exchange',
369 369 default=list,
370 370 )
371 371 coreconfigitem('devel', 'servercafile',
372 372 default='',
373 373 )
374 374 coreconfigitem('devel', 'serverexactprotocol',
375 375 default='',
376 376 )
377 377 coreconfigitem('devel', 'serverrequirecert',
378 378 default=False,
379 379 )
380 380 coreconfigitem('devel', 'strip-obsmarkers',
381 381 default=True,
382 382 )
383 383 coreconfigitem('devel', 'warn-config',
384 384 default=None,
385 385 )
386 386 coreconfigitem('devel', 'warn-config-default',
387 387 default=None,
388 388 )
389 389 coreconfigitem('devel', 'user.obsmarker',
390 390 default=None,
391 391 )
392 392 coreconfigitem('devel', 'warn-config-unknown',
393 393 default=None,
394 394 )
395 395 coreconfigitem('devel', 'debug.copies',
396 396 default=False,
397 397 )
398 398 coreconfigitem('devel', 'debug.extensions',
399 399 default=False,
400 400 )
401 401 coreconfigitem('devel', 'debug.peer-request',
402 402 default=False,
403 403 )
404 404 coreconfigitem('diff', 'nodates',
405 405 default=False,
406 406 )
407 407 coreconfigitem('diff', 'showfunc',
408 408 default=False,
409 409 )
410 410 coreconfigitem('diff', 'unified',
411 411 default=None,
412 412 )
413 413 coreconfigitem('diff', 'git',
414 414 default=False,
415 415 )
416 416 coreconfigitem('diff', 'ignorews',
417 417 default=False,
418 418 )
419 419 coreconfigitem('diff', 'ignorewsamount',
420 420 default=False,
421 421 )
422 422 coreconfigitem('diff', 'ignoreblanklines',
423 423 default=False,
424 424 )
425 425 coreconfigitem('diff', 'ignorewseol',
426 426 default=False,
427 427 )
428 428 coreconfigitem('diff', 'nobinary',
429 429 default=False,
430 430 )
431 431 coreconfigitem('diff', 'noprefix',
432 432 default=False,
433 433 )
434 434 coreconfigitem('diff', 'word-diff',
435 435 default=False,
436 436 )
437 437 coreconfigitem('email', 'bcc',
438 438 default=None,
439 439 )
440 440 coreconfigitem('email', 'cc',
441 441 default=None,
442 442 )
443 443 coreconfigitem('email', 'charsets',
444 444 default=list,
445 445 )
446 446 coreconfigitem('email', 'from',
447 447 default=None,
448 448 )
449 449 coreconfigitem('email', 'method',
450 450 default='smtp',
451 451 )
452 452 coreconfigitem('email', 'reply-to',
453 453 default=None,
454 454 )
455 455 coreconfigitem('email', 'to',
456 456 default=None,
457 457 )
458 458 coreconfigitem('experimental', 'archivemetatemplate',
459 459 default=dynamicdefault,
460 460 )
461 461 coreconfigitem('experimental', 'auto-publish',
462 462 default='publish',
463 463 )
464 464 coreconfigitem('experimental', 'bundle-phases',
465 465 default=False,
466 466 )
467 467 coreconfigitem('experimental', 'bundle2-advertise',
468 468 default=True,
469 469 )
470 470 coreconfigitem('experimental', 'bundle2-output-capture',
471 471 default=False,
472 472 )
473 473 coreconfigitem('experimental', 'bundle2.pushback',
474 474 default=False,
475 475 )
476 476 coreconfigitem('experimental', 'bundle2lazylocking',
477 477 default=False,
478 478 )
479 479 coreconfigitem('experimental', 'bundlecomplevel',
480 480 default=None,
481 481 )
482 482 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
483 483 default=None,
484 484 )
485 485 coreconfigitem('experimental', 'bundlecomplevel.gzip',
486 486 default=None,
487 487 )
488 488 coreconfigitem('experimental', 'bundlecomplevel.none',
489 489 default=None,
490 490 )
491 491 coreconfigitem('experimental', 'bundlecomplevel.zstd',
492 492 default=None,
493 493 )
494 494 coreconfigitem('experimental', 'changegroup3',
495 495 default=False,
496 496 )
497 497 coreconfigitem('experimental', 'clientcompressionengines',
498 498 default=list,
499 499 )
500 500 coreconfigitem('experimental', 'copytrace',
501 501 default='on',
502 502 )
503 503 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
504 504 default=100,
505 505 )
506 506 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
507 507 default=100,
508 508 )
509 509 coreconfigitem('experimental', 'crecordtest',
510 510 default=None,
511 511 )
512 512 coreconfigitem('experimental', 'directaccess',
513 513 default=False,
514 514 )
515 515 coreconfigitem('experimental', 'directaccess.revnums',
516 516 default=False,
517 517 )
518 518 coreconfigitem('experimental', 'editortmpinhg',
519 519 default=False,
520 520 )
521 521 coreconfigitem('experimental', 'evolution',
522 522 default=list,
523 523 )
524 524 coreconfigitem('experimental', 'evolution.allowdivergence',
525 525 default=False,
526 526 alias=[('experimental', 'allowdivergence')]
527 527 )
528 528 coreconfigitem('experimental', 'evolution.allowunstable',
529 529 default=None,
530 530 )
531 531 coreconfigitem('experimental', 'evolution.createmarkers',
532 532 default=None,
533 533 )
534 534 coreconfigitem('experimental', 'evolution.effect-flags',
535 535 default=True,
536 536 alias=[('experimental', 'effect-flags')]
537 537 )
538 538 coreconfigitem('experimental', 'evolution.exchange',
539 539 default=None,
540 540 )
541 541 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
542 542 default=False,
543 543 )
544 544 coreconfigitem('experimental', 'evolution.report-instabilities',
545 545 default=True,
546 546 )
547 547 coreconfigitem('experimental', 'evolution.track-operation',
548 548 default=True,
549 549 )
550 550 coreconfigitem('experimental', 'maxdeltachainspan',
551 551 default=-1,
552 552 )
553 553 coreconfigitem('experimental', 'mergetempdirprefix',
554 554 default=None,
555 555 )
556 556 coreconfigitem('experimental', 'narrow',
557 557 default=False,
558 558 )
559 559 coreconfigitem('experimental', 'nonnormalparanoidcheck',
560 560 default=False,
561 561 )
562 562 coreconfigitem('experimental', 'exportableenviron',
563 563 default=list,
564 564 )
565 565 coreconfigitem('experimental', 'extendedheader.index',
566 566 default=None,
567 567 )
568 568 coreconfigitem('experimental', 'extendedheader.similarity',
569 569 default=False,
570 570 )
571 571 coreconfigitem('experimental', 'format.compression',
572 572 default='zlib',
573 573 )
574 574 coreconfigitem('experimental', 'graphshorten',
575 575 default=False,
576 576 )
577 577 coreconfigitem('experimental', 'graphstyle.parent',
578 578 default=dynamicdefault,
579 579 )
580 580 coreconfigitem('experimental', 'graphstyle.missing',
581 581 default=dynamicdefault,
582 582 )
583 583 coreconfigitem('experimental', 'graphstyle.grandparent',
584 584 default=dynamicdefault,
585 585 )
586 586 coreconfigitem('experimental', 'hook-track-tags',
587 587 default=False,
588 588 )
589 589 coreconfigitem('experimental', 'httppeer.advertise-v2',
590 590 default=False,
591 591 )
592 592 coreconfigitem('experimental', 'httppeer.v2-encoder-order',
593 593 default=None,
594 594 )
595 595 coreconfigitem('experimental', 'httppostargs',
596 596 default=False,
597 597 )
598 598 coreconfigitem('experimental', 'mergedriver',
599 599 default=None,
600 600 )
601 601 coreconfigitem('experimental', 'nointerrupt', default=False)
602 602 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
603 603
604 604 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
605 605 default=False,
606 606 )
607 607 coreconfigitem('experimental', 'remotenames',
608 608 default=False,
609 609 )
610 610 coreconfigitem('experimental', 'removeemptydirs',
611 611 default=True,
612 612 )
613 613 coreconfigitem('experimental', 'revisions.prefixhexnode',
614 614 default=False,
615 615 )
616 616 coreconfigitem('experimental', 'revlogv2',
617 617 default=None,
618 618 )
619 619 coreconfigitem('experimental', 'revisions.disambiguatewithin',
620 620 default=None,
621 621 )
622 622 coreconfigitem('experimental', 'server.filesdata.recommended-batch-size',
623 623 default=50000,
624 624 )
625 625 coreconfigitem('experimental', 'server.manifestdata.recommended-batch-size',
626 626 default=100000,
627 627 )
628 628 coreconfigitem('experimental', 'server.stream-narrow-clones',
629 629 default=False,
630 630 )
631 631 coreconfigitem('experimental', 'single-head-per-branch',
632 632 default=False,
633 633 )
634 634 coreconfigitem('experimental', 'sshserver.support-v2',
635 635 default=False,
636 636 )
637 637 coreconfigitem('experimental', 'sparse-read',
638 638 default=False,
639 639 )
640 640 coreconfigitem('experimental', 'sparse-read.density-threshold',
641 641 default=0.50,
642 642 )
643 643 coreconfigitem('experimental', 'sparse-read.min-gap-size',
644 644 default='65K',
645 645 )
646 646 coreconfigitem('experimental', 'treemanifest',
647 647 default=False,
648 648 )
649 649 coreconfigitem('experimental', 'update.atomic-file',
650 650 default=False,
651 651 )
652 652 coreconfigitem('experimental', 'sshpeer.advertise-v2',
653 653 default=False,
654 654 )
655 655 coreconfigitem('experimental', 'web.apiserver',
656 656 default=False,
657 657 )
658 658 coreconfigitem('experimental', 'web.api.http-v2',
659 659 default=False,
660 660 )
661 661 coreconfigitem('experimental', 'web.api.debugreflect',
662 662 default=False,
663 663 )
664 664 coreconfigitem('experimental', 'worker.wdir-get-thread-safe',
665 665 default=False,
666 666 )
667 667 coreconfigitem('experimental', 'xdiff',
668 668 default=False,
669 669 )
670 670 coreconfigitem('extensions', '.*',
671 671 default=None,
672 672 generic=True,
673 673 )
674 674 coreconfigitem('extdata', '.*',
675 675 default=None,
676 676 generic=True,
677 677 )
678 678 coreconfigitem('format', 'chunkcachesize',
679 679 default=None,
680 680 )
681 681 coreconfigitem('format', 'dotencode',
682 682 default=True,
683 683 )
684 684 coreconfigitem('format', 'generaldelta',
685 685 default=False,
686 686 )
687 687 coreconfigitem('format', 'manifestcachesize',
688 688 default=None,
689 689 )
690 690 coreconfigitem('format', 'maxchainlen',
691 691 default=dynamicdefault,
692 692 )
693 693 coreconfigitem('format', 'obsstore-version',
694 694 default=None,
695 695 )
696 696 coreconfigitem('format', 'sparse-revlog',
697 697 default=True,
698 698 )
699 699 coreconfigitem('format', 'usefncache',
700 700 default=True,
701 701 )
702 702 coreconfigitem('format', 'usegeneraldelta',
703 703 default=True,
704 704 )
705 705 coreconfigitem('format', 'usestore',
706 706 default=True,
707 707 )
708 708 coreconfigitem('format', 'internal-phase',
709 709 default=False,
710 710 )
711 711 coreconfigitem('fsmonitor', 'warn_when_unused',
712 712 default=True,
713 713 )
714 714 coreconfigitem('fsmonitor', 'warn_update_file_count',
715 715 default=50000,
716 716 )
717 717 coreconfigitem('help', 'hidden-command\..*',
718 718 default=False,
719 719 generic=True,
720 720 )
721 721 coreconfigitem('help', 'hidden-topic\..*',
722 722 default=False,
723 723 generic=True,
724 724 )
725 725 coreconfigitem('hooks', '.*',
726 726 default=dynamicdefault,
727 727 generic=True,
728 728 )
729 729 coreconfigitem('hgweb-paths', '.*',
730 730 default=list,
731 731 generic=True,
732 732 )
733 733 coreconfigitem('hostfingerprints', '.*',
734 734 default=list,
735 735 generic=True,
736 736 )
737 737 coreconfigitem('hostsecurity', 'ciphers',
738 738 default=None,
739 739 )
740 740 coreconfigitem('hostsecurity', 'disabletls10warning',
741 741 default=False,
742 742 )
743 743 coreconfigitem('hostsecurity', 'minimumprotocol',
744 744 default=dynamicdefault,
745 745 )
746 746 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
747 747 default=dynamicdefault,
748 748 generic=True,
749 749 )
750 750 coreconfigitem('hostsecurity', '.*:ciphers$',
751 751 default=dynamicdefault,
752 752 generic=True,
753 753 )
754 754 coreconfigitem('hostsecurity', '.*:fingerprints$',
755 755 default=list,
756 756 generic=True,
757 757 )
758 758 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
759 759 default=None,
760 760 generic=True,
761 761 )
762 762
763 763 coreconfigitem('http_proxy', 'always',
764 764 default=False,
765 765 )
766 766 coreconfigitem('http_proxy', 'host',
767 767 default=None,
768 768 )
769 769 coreconfigitem('http_proxy', 'no',
770 770 default=list,
771 771 )
772 772 coreconfigitem('http_proxy', 'passwd',
773 773 default=None,
774 774 )
775 775 coreconfigitem('http_proxy', 'user',
776 776 default=None,
777 777 )
778 778
779 779 coreconfigitem('http', 'timeout',
780 780 default=None,
781 781 )
782 782
783 783 coreconfigitem('logtoprocess', 'commandexception',
784 784 default=None,
785 785 )
786 786 coreconfigitem('logtoprocess', 'commandfinish',
787 787 default=None,
788 788 )
789 789 coreconfigitem('logtoprocess', 'command',
790 790 default=None,
791 791 )
792 792 coreconfigitem('logtoprocess', 'develwarn',
793 793 default=None,
794 794 )
795 795 coreconfigitem('logtoprocess', 'uiblocked',
796 796 default=None,
797 797 )
798 798 coreconfigitem('merge', 'checkunknown',
799 799 default='abort',
800 800 )
801 801 coreconfigitem('merge', 'checkignored',
802 802 default='abort',
803 803 )
804 804 coreconfigitem('experimental', 'merge.checkpathconflicts',
805 805 default=False,
806 806 )
807 807 coreconfigitem('merge', 'followcopies',
808 808 default=True,
809 809 )
810 810 coreconfigitem('merge', 'on-failure',
811 811 default='continue',
812 812 )
813 813 coreconfigitem('merge', 'preferancestor',
814 814 default=lambda: ['*'],
815 815 )
816 816 coreconfigitem('merge', 'strict-capability-check',
817 817 default=False,
818 818 )
819 819 coreconfigitem('merge-tools', '.*',
820 820 default=None,
821 821 generic=True,
822 822 )
823 823 coreconfigitem('merge-tools', br'.*\.args$',
824 824 default="$local $base $other",
825 825 generic=True,
826 826 priority=-1,
827 827 )
828 828 coreconfigitem('merge-tools', br'.*\.binary$',
829 829 default=False,
830 830 generic=True,
831 831 priority=-1,
832 832 )
833 833 coreconfigitem('merge-tools', br'.*\.check$',
834 834 default=list,
835 835 generic=True,
836 836 priority=-1,
837 837 )
838 838 coreconfigitem('merge-tools', br'.*\.checkchanged$',
839 839 default=False,
840 840 generic=True,
841 841 priority=-1,
842 842 )
843 843 coreconfigitem('merge-tools', br'.*\.executable$',
844 844 default=dynamicdefault,
845 845 generic=True,
846 846 priority=-1,
847 847 )
848 848 coreconfigitem('merge-tools', br'.*\.fixeol$',
849 849 default=False,
850 850 generic=True,
851 851 priority=-1,
852 852 )
853 853 coreconfigitem('merge-tools', br'.*\.gui$',
854 854 default=False,
855 855 generic=True,
856 856 priority=-1,
857 857 )
858 858 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
859 859 default='basic',
860 860 generic=True,
861 861 priority=-1,
862 862 )
863 863 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
864 864 default=dynamicdefault, # take from ui.mergemarkertemplate
865 865 generic=True,
866 866 priority=-1,
867 867 )
868 868 coreconfigitem('merge-tools', br'.*\.priority$',
869 869 default=0,
870 870 generic=True,
871 871 priority=-1,
872 872 )
873 873 coreconfigitem('merge-tools', br'.*\.premerge$',
874 874 default=dynamicdefault,
875 875 generic=True,
876 876 priority=-1,
877 877 )
878 878 coreconfigitem('merge-tools', br'.*\.symlink$',
879 879 default=False,
880 880 generic=True,
881 881 priority=-1,
882 882 )
883 883 coreconfigitem('pager', 'attend-.*',
884 884 default=dynamicdefault,
885 885 generic=True,
886 886 )
887 887 coreconfigitem('pager', 'ignore',
888 888 default=list,
889 889 )
890 890 coreconfigitem('pager', 'pager',
891 891 default=dynamicdefault,
892 892 )
893 893 coreconfigitem('patch', 'eol',
894 894 default='strict',
895 895 )
896 896 coreconfigitem('patch', 'fuzz',
897 897 default=2,
898 898 )
899 899 coreconfigitem('paths', 'default',
900 900 default=None,
901 901 )
902 902 coreconfigitem('paths', 'default-push',
903 903 default=None,
904 904 )
905 905 coreconfigitem('paths', '.*',
906 906 default=None,
907 907 generic=True,
908 908 )
909 909 coreconfigitem('phases', 'checksubrepos',
910 910 default='follow',
911 911 )
912 912 coreconfigitem('phases', 'new-commit',
913 913 default='draft',
914 914 )
915 915 coreconfigitem('phases', 'publish',
916 916 default=True,
917 917 )
918 918 coreconfigitem('profiling', 'enabled',
919 919 default=False,
920 920 )
921 921 coreconfigitem('profiling', 'format',
922 922 default='text',
923 923 )
924 924 coreconfigitem('profiling', 'freq',
925 925 default=1000,
926 926 )
927 927 coreconfigitem('profiling', 'limit',
928 928 default=30,
929 929 )
930 930 coreconfigitem('profiling', 'nested',
931 931 default=0,
932 932 )
933 933 coreconfigitem('profiling', 'output',
934 934 default=None,
935 935 )
936 936 coreconfigitem('profiling', 'showmax',
937 937 default=0.999,
938 938 )
939 939 coreconfigitem('profiling', 'showmin',
940 940 default=dynamicdefault,
941 941 )
942 942 coreconfigitem('profiling', 'sort',
943 943 default='inlinetime',
944 944 )
945 945 coreconfigitem('profiling', 'statformat',
946 946 default='hotpath',
947 947 )
948 948 coreconfigitem('profiling', 'time-track',
949 949 default=dynamicdefault,
950 950 )
951 951 coreconfigitem('profiling', 'type',
952 952 default='stat',
953 953 )
954 954 coreconfigitem('progress', 'assume-tty',
955 955 default=False,
956 956 )
957 957 coreconfigitem('progress', 'changedelay',
958 958 default=1,
959 959 )
960 960 coreconfigitem('progress', 'clear-complete',
961 961 default=True,
962 962 )
963 963 coreconfigitem('progress', 'debug',
964 964 default=False,
965 965 )
966 966 coreconfigitem('progress', 'delay',
967 967 default=3,
968 968 )
969 969 coreconfigitem('progress', 'disable',
970 970 default=False,
971 971 )
972 972 coreconfigitem('progress', 'estimateinterval',
973 973 default=60.0,
974 974 )
975 975 coreconfigitem('progress', 'format',
976 976 default=lambda: ['topic', 'bar', 'number', 'estimate'],
977 977 )
978 978 coreconfigitem('progress', 'refresh',
979 979 default=0.1,
980 980 )
981 981 coreconfigitem('progress', 'width',
982 982 default=dynamicdefault,
983 983 )
984 984 coreconfigitem('push', 'pushvars.server',
985 985 default=False,
986 986 )
987 987 coreconfigitem('storage', 'mmap-threshold',
988 988 default='1MB',
989 989 alias=[('experimental', 'mmapindexthreshold')],
990 990 )
991 991 coreconfigitem('storage', 'new-repo-backend',
992 992 default='revlogv1',
993 993 )
994 994 coreconfigitem('storage', 'revlog.optimize-delta-parent-choice',
995 995 default=True,
996 996 alias=[('format', 'aggressivemergedeltas')],
997 997 )
998 998 coreconfigitem('server', 'bookmarks-pushkey-compat',
999 999 default=True,
1000 1000 )
1001 1001 coreconfigitem('server', 'bundle1',
1002 1002 default=True,
1003 1003 )
1004 1004 coreconfigitem('server', 'bundle1gd',
1005 1005 default=None,
1006 1006 )
1007 1007 coreconfigitem('server', 'bundle1.pull',
1008 1008 default=None,
1009 1009 )
1010 1010 coreconfigitem('server', 'bundle1gd.pull',
1011 1011 default=None,
1012 1012 )
1013 1013 coreconfigitem('server', 'bundle1.push',
1014 1014 default=None,
1015 1015 )
1016 1016 coreconfigitem('server', 'bundle1gd.push',
1017 1017 default=None,
1018 1018 )
1019 1019 coreconfigitem('server', 'bundle2.stream',
1020 1020 default=True,
1021 1021 alias=[('experimental', 'bundle2.stream')]
1022 1022 )
1023 1023 coreconfigitem('server', 'compressionengines',
1024 1024 default=list,
1025 1025 )
1026 1026 coreconfigitem('server', 'concurrent-push-mode',
1027 1027 default='strict',
1028 1028 )
1029 1029 coreconfigitem('server', 'disablefullbundle',
1030 1030 default=False,
1031 1031 )
1032 1032 coreconfigitem('server', 'maxhttpheaderlen',
1033 1033 default=1024,
1034 1034 )
1035 1035 coreconfigitem('server', 'pullbundle',
1036 1036 default=False,
1037 1037 )
1038 1038 coreconfigitem('server', 'preferuncompressed',
1039 1039 default=False,
1040 1040 )
1041 1041 coreconfigitem('server', 'streamunbundle',
1042 1042 default=False,
1043 1043 )
1044 1044 coreconfigitem('server', 'uncompressed',
1045 1045 default=True,
1046 1046 )
1047 1047 coreconfigitem('server', 'uncompressedallowsecret',
1048 1048 default=False,
1049 1049 )
1050 1050 coreconfigitem('server', 'validate',
1051 1051 default=False,
1052 1052 )
1053 1053 coreconfigitem('server', 'zliblevel',
1054 1054 default=-1,
1055 1055 )
1056 1056 coreconfigitem('server', 'zstdlevel',
1057 1057 default=3,
1058 1058 )
1059 1059 coreconfigitem('share', 'pool',
1060 1060 default=None,
1061 1061 )
1062 1062 coreconfigitem('share', 'poolnaming',
1063 1063 default='identity',
1064 1064 )
1065 1065 coreconfigitem('smtp', 'host',
1066 1066 default=None,
1067 1067 )
1068 1068 coreconfigitem('smtp', 'local_hostname',
1069 1069 default=None,
1070 1070 )
1071 1071 coreconfigitem('smtp', 'password',
1072 1072 default=None,
1073 1073 )
1074 1074 coreconfigitem('smtp', 'port',
1075 1075 default=dynamicdefault,
1076 1076 )
1077 1077 coreconfigitem('smtp', 'tls',
1078 1078 default='none',
1079 1079 )
1080 1080 coreconfigitem('smtp', 'username',
1081 1081 default=None,
1082 1082 )
1083 1083 coreconfigitem('sparse', 'missingwarning',
1084 1084 default=True,
1085 1085 )
1086 1086 coreconfigitem('subrepos', 'allowed',
1087 1087 default=dynamicdefault, # to make backporting simpler
1088 1088 )
1089 1089 coreconfigitem('subrepos', 'hg:allowed',
1090 1090 default=dynamicdefault,
1091 1091 )
1092 1092 coreconfigitem('subrepos', 'git:allowed',
1093 1093 default=dynamicdefault,
1094 1094 )
1095 1095 coreconfigitem('subrepos', 'svn:allowed',
1096 1096 default=dynamicdefault,
1097 1097 )
1098 1098 coreconfigitem('templates', '.*',
1099 1099 default=None,
1100 1100 generic=True,
1101 1101 )
1102 1102 coreconfigitem('trusted', 'groups',
1103 1103 default=list,
1104 1104 )
1105 1105 coreconfigitem('trusted', 'users',
1106 1106 default=list,
1107 1107 )
1108 1108 coreconfigitem('ui', '_usedassubrepo',
1109 1109 default=False,
1110 1110 )
1111 1111 coreconfigitem('ui', 'allowemptycommit',
1112 1112 default=False,
1113 1113 )
1114 1114 coreconfigitem('ui', 'archivemeta',
1115 1115 default=True,
1116 1116 )
1117 1117 coreconfigitem('ui', 'askusername',
1118 1118 default=False,
1119 1119 )
1120 1120 coreconfigitem('ui', 'clonebundlefallback',
1121 1121 default=False,
1122 1122 )
1123 1123 coreconfigitem('ui', 'clonebundleprefers',
1124 1124 default=list,
1125 1125 )
1126 1126 coreconfigitem('ui', 'clonebundles',
1127 1127 default=True,
1128 1128 )
1129 1129 coreconfigitem('ui', 'color',
1130 1130 default='auto',
1131 1131 )
1132 1132 coreconfigitem('ui', 'commitsubrepos',
1133 1133 default=False,
1134 1134 )
1135 1135 coreconfigitem('ui', 'debug',
1136 1136 default=False,
1137 1137 )
1138 1138 coreconfigitem('ui', 'debugger',
1139 1139 default=None,
1140 1140 )
1141 1141 coreconfigitem('ui', 'editor',
1142 1142 default=dynamicdefault,
1143 1143 )
1144 1144 coreconfigitem('ui', 'fallbackencoding',
1145 1145 default=None,
1146 1146 )
1147 1147 coreconfigitem('ui', 'forcecwd',
1148 1148 default=None,
1149 1149 )
1150 1150 coreconfigitem('ui', 'forcemerge',
1151 1151 default=None,
1152 1152 )
1153 1153 coreconfigitem('ui', 'formatdebug',
1154 1154 default=False,
1155 1155 )
1156 1156 coreconfigitem('ui', 'formatjson',
1157 1157 default=False,
1158 1158 )
1159 1159 coreconfigitem('ui', 'formatted',
1160 1160 default=None,
1161 1161 )
1162 1162 coreconfigitem('ui', 'graphnodetemplate',
1163 1163 default=None,
1164 1164 )
1165 1165 coreconfigitem('ui', 'history-editing-backup',
1166 1166 default=True,
1167 1167 )
1168 1168 coreconfigitem('ui', 'interactive',
1169 1169 default=None,
1170 1170 )
1171 1171 coreconfigitem('ui', 'interface',
1172 1172 default=None,
1173 1173 )
1174 1174 coreconfigitem('ui', 'interface.chunkselector',
1175 1175 default=None,
1176 1176 )
1177 1177 coreconfigitem('ui', 'large-file-limit',
1178 1178 default=10000000,
1179 1179 )
1180 1180 coreconfigitem('ui', 'logblockedtimes',
1181 1181 default=False,
1182 1182 )
1183 1183 coreconfigitem('ui', 'logtemplate',
1184 1184 default=None,
1185 1185 )
1186 1186 coreconfigitem('ui', 'merge',
1187 1187 default=None,
1188 1188 )
1189 1189 coreconfigitem('ui', 'mergemarkers',
1190 1190 default='basic',
1191 1191 )
1192 1192 coreconfigitem('ui', 'mergemarkertemplate',
1193 1193 default=('{node|short} '
1194 1194 '{ifeq(tags, "tip", "", '
1195 1195 'ifeq(tags, "", "", "{tags} "))}'
1196 1196 '{if(bookmarks, "{bookmarks} ")}'
1197 1197 '{ifeq(branch, "default", "", "{branch} ")}'
1198 1198 '- {author|user}: {desc|firstline}')
1199 1199 )
1200 1200 coreconfigitem('ui', 'message-output',
1201 1201 default='stdio',
1202 1202 )
1203 1203 coreconfigitem('ui', 'nontty',
1204 1204 default=False,
1205 1205 )
1206 1206 coreconfigitem('ui', 'origbackuppath',
1207 1207 default=None,
1208 1208 )
1209 1209 coreconfigitem('ui', 'paginate',
1210 1210 default=True,
1211 1211 )
1212 1212 coreconfigitem('ui', 'patch',
1213 1213 default=None,
1214 1214 )
1215 1215 coreconfigitem('ui', 'pre-merge-tool-output-template',
1216 1216 default=None,
1217 1217 )
1218 1218 coreconfigitem('ui', 'portablefilenames',
1219 1219 default='warn',
1220 1220 )
1221 1221 coreconfigitem('ui', 'promptecho',
1222 1222 default=False,
1223 1223 )
1224 1224 coreconfigitem('ui', 'quiet',
1225 1225 default=False,
1226 1226 )
1227 1227 coreconfigitem('ui', 'quietbookmarkmove',
1228 1228 default=False,
1229 1229 )
1230 1230 coreconfigitem('ui', 'remotecmd',
1231 1231 default='hg',
1232 1232 )
1233 1233 coreconfigitem('ui', 'report_untrusted',
1234 1234 default=True,
1235 1235 )
1236 1236 coreconfigitem('ui', 'rollback',
1237 1237 default=True,
1238 1238 )
1239 1239 coreconfigitem('ui', 'signal-safe-lock',
1240 1240 default=True,
1241 1241 )
1242 1242 coreconfigitem('ui', 'slash',
1243 1243 default=False,
1244 1244 )
1245 1245 coreconfigitem('ui', 'ssh',
1246 1246 default='ssh',
1247 1247 )
1248 1248 coreconfigitem('ui', 'ssherrorhint',
1249 1249 default=None,
1250 1250 )
1251 1251 coreconfigitem('ui', 'statuscopies',
1252 1252 default=False,
1253 1253 )
1254 1254 coreconfigitem('ui', 'strict',
1255 1255 default=False,
1256 1256 )
1257 1257 coreconfigitem('ui', 'style',
1258 1258 default='',
1259 1259 )
1260 1260 coreconfigitem('ui', 'supportcontact',
1261 1261 default=None,
1262 1262 )
1263 1263 coreconfigitem('ui', 'textwidth',
1264 1264 default=78,
1265 1265 )
1266 1266 coreconfigitem('ui', 'timeout',
1267 1267 default='600',
1268 1268 )
1269 1269 coreconfigitem('ui', 'timeout.warn',
1270 1270 default=0,
1271 1271 )
1272 1272 coreconfigitem('ui', 'traceback',
1273 1273 default=False,
1274 1274 )
1275 1275 coreconfigitem('ui', 'tweakdefaults',
1276 1276 default=False,
1277 1277 )
1278 1278 coreconfigitem('ui', 'username',
1279 1279 alias=[('ui', 'user')]
1280 1280 )
1281 1281 coreconfigitem('ui', 'verbose',
1282 1282 default=False,
1283 1283 )
1284 1284 coreconfigitem('verify', 'skipflags',
1285 1285 default=None,
1286 1286 )
1287 1287 coreconfigitem('web', 'allowbz2',
1288 1288 default=False,
1289 1289 )
1290 1290 coreconfigitem('web', 'allowgz',
1291 1291 default=False,
1292 1292 )
1293 1293 coreconfigitem('web', 'allow-pull',
1294 1294 alias=[('web', 'allowpull')],
1295 1295 default=True,
1296 1296 )
1297 1297 coreconfigitem('web', 'allow-push',
1298 1298 alias=[('web', 'allow_push')],
1299 1299 default=list,
1300 1300 )
1301 1301 coreconfigitem('web', 'allowzip',
1302 1302 default=False,
1303 1303 )
1304 1304 coreconfigitem('web', 'archivesubrepos',
1305 1305 default=False,
1306 1306 )
1307 1307 coreconfigitem('web', 'cache',
1308 1308 default=True,
1309 1309 )
1310 coreconfigitem('web', 'comparisoncontext',
1311 default=5,
1312 )
1310 1313 coreconfigitem('web', 'contact',
1311 1314 default=None,
1312 1315 )
1313 1316 coreconfigitem('web', 'deny_push',
1314 1317 default=list,
1315 1318 )
1316 1319 coreconfigitem('web', 'guessmime',
1317 1320 default=False,
1318 1321 )
1319 1322 coreconfigitem('web', 'hidden',
1320 1323 default=False,
1321 1324 )
1322 1325 coreconfigitem('web', 'labels',
1323 1326 default=list,
1324 1327 )
1325 1328 coreconfigitem('web', 'logoimg',
1326 1329 default='hglogo.png',
1327 1330 )
1328 1331 coreconfigitem('web', 'logourl',
1329 1332 default='https://mercurial-scm.org/',
1330 1333 )
1331 1334 coreconfigitem('web', 'accesslog',
1332 1335 default='-',
1333 1336 )
1334 1337 coreconfigitem('web', 'address',
1335 1338 default='',
1336 1339 )
1337 1340 coreconfigitem('web', 'allow-archive',
1338 1341 alias=[('web', 'allow_archive')],
1339 1342 default=list,
1340 1343 )
1341 1344 coreconfigitem('web', 'allow_read',
1342 1345 default=list,
1343 1346 )
1344 1347 coreconfigitem('web', 'baseurl',
1345 1348 default=None,
1346 1349 )
1347 1350 coreconfigitem('web', 'cacerts',
1348 1351 default=None,
1349 1352 )
1350 1353 coreconfigitem('web', 'certificate',
1351 1354 default=None,
1352 1355 )
1353 1356 coreconfigitem('web', 'collapse',
1354 1357 default=False,
1355 1358 )
1356 1359 coreconfigitem('web', 'csp',
1357 1360 default=None,
1358 1361 )
1359 1362 coreconfigitem('web', 'deny_read',
1360 1363 default=list,
1361 1364 )
1362 1365 coreconfigitem('web', 'descend',
1363 1366 default=True,
1364 1367 )
1365 1368 coreconfigitem('web', 'description',
1366 1369 default="",
1367 1370 )
1368 1371 coreconfigitem('web', 'encoding',
1369 1372 default=lambda: encoding.encoding,
1370 1373 )
1371 1374 coreconfigitem('web', 'errorlog',
1372 1375 default='-',
1373 1376 )
1374 1377 coreconfigitem('web', 'ipv6',
1375 1378 default=False,
1376 1379 )
1377 1380 coreconfigitem('web', 'maxchanges',
1378 1381 default=10,
1379 1382 )
1380 1383 coreconfigitem('web', 'maxfiles',
1381 1384 default=10,
1382 1385 )
1383 1386 coreconfigitem('web', 'maxshortchanges',
1384 1387 default=60,
1385 1388 )
1386 1389 coreconfigitem('web', 'motd',
1387 1390 default='',
1388 1391 )
1389 1392 coreconfigitem('web', 'name',
1390 1393 default=dynamicdefault,
1391 1394 )
1392 1395 coreconfigitem('web', 'port',
1393 1396 default=8000,
1394 1397 )
1395 1398 coreconfigitem('web', 'prefix',
1396 1399 default='',
1397 1400 )
1398 1401 coreconfigitem('web', 'push_ssl',
1399 1402 default=True,
1400 1403 )
1401 1404 coreconfigitem('web', 'refreshinterval',
1402 1405 default=20,
1403 1406 )
1404 1407 coreconfigitem('web', 'server-header',
1405 1408 default=None,
1406 1409 )
1407 1410 coreconfigitem('web', 'static',
1408 1411 default=None,
1409 1412 )
1410 1413 coreconfigitem('web', 'staticurl',
1411 1414 default=None,
1412 1415 )
1413 1416 coreconfigitem('web', 'stripes',
1414 1417 default=1,
1415 1418 )
1416 1419 coreconfigitem('web', 'style',
1417 1420 default='paper',
1418 1421 )
1419 1422 coreconfigitem('web', 'templates',
1420 1423 default=None,
1421 1424 )
1422 1425 coreconfigitem('web', 'view',
1423 1426 default='served',
1424 1427 )
1425 1428 coreconfigitem('worker', 'backgroundclose',
1426 1429 default=dynamicdefault,
1427 1430 )
1428 1431 # Windows defaults to a limit of 512 open files. A buffer of 128
1429 1432 # should give us enough headway.
1430 1433 coreconfigitem('worker', 'backgroundclosemaxqueue',
1431 1434 default=384,
1432 1435 )
1433 1436 coreconfigitem('worker', 'backgroundcloseminfilecount',
1434 1437 default=2048,
1435 1438 )
1436 1439 coreconfigitem('worker', 'backgroundclosethreadcount',
1437 1440 default=4,
1438 1441 )
1439 1442 coreconfigitem('worker', 'enabled',
1440 1443 default=True,
1441 1444 )
1442 1445 coreconfigitem('worker', 'numcpus',
1443 1446 default=None,
1444 1447 )
1445 1448
1446 1449 # Rebase related configuration moved to core because other extension are doing
1447 1450 # strange things. For example, shelve import the extensions to reuse some bit
1448 1451 # without formally loading it.
1449 1452 coreconfigitem('commands', 'rebase.requiredest',
1450 1453 default=False,
1451 1454 )
1452 1455 coreconfigitem('experimental', 'rebaseskipobsolete',
1453 1456 default=True,
1454 1457 )
1455 1458 coreconfigitem('rebase', 'singletransaction',
1456 1459 default=False,
1457 1460 )
1458 1461 coreconfigitem('rebase', 'experimental.inmemory',
1459 1462 default=False,
1460 1463 )
@@ -1,1484 +1,1484 b''
1 1 #
2 2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
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 copy
11 11 import mimetypes
12 12 import os
13 13 import re
14 14
15 15 from ..i18n import _
16 16 from ..node import hex, short
17 17
18 18 from .common import (
19 19 ErrorResponse,
20 20 HTTP_FORBIDDEN,
21 21 HTTP_NOT_FOUND,
22 22 get_contact,
23 23 paritygen,
24 24 staticfile,
25 25 )
26 26
27 27 from .. import (
28 28 archival,
29 29 dagop,
30 30 encoding,
31 31 error,
32 32 graphmod,
33 33 pycompat,
34 34 revset,
35 35 revsetlang,
36 36 scmutil,
37 37 smartset,
38 38 templater,
39 39 templateutil,
40 40 )
41 41
42 42 from ..utils import (
43 43 stringutil,
44 44 )
45 45
46 46 from . import (
47 47 webutil,
48 48 )
49 49
50 50 __all__ = []
51 51 commands = {}
52 52
53 53 class webcommand(object):
54 54 """Decorator used to register a web command handler.
55 55
56 56 The decorator takes as its positional arguments the name/path the
57 57 command should be accessible under.
58 58
59 59 When called, functions receive as arguments a ``requestcontext``,
60 60 ``wsgirequest``, and a templater instance for generatoring output.
61 61 The functions should populate the ``rctx.res`` object with details
62 62 about the HTTP response.
63 63
64 64 The function returns a generator to be consumed by the WSGI application.
65 65 For most commands, this should be the result from
66 66 ``web.res.sendresponse()``. Many commands will call ``web.sendtemplate()``
67 67 to render a template.
68 68
69 69 Usage:
70 70
71 71 @webcommand('mycommand')
72 72 def mycommand(web):
73 73 pass
74 74 """
75 75
76 76 def __init__(self, name):
77 77 self.name = name
78 78
79 79 def __call__(self, func):
80 80 __all__.append(self.name)
81 81 commands[self.name] = func
82 82 return func
83 83
84 84 @webcommand('log')
85 85 def log(web):
86 86 """
87 87 /log[/{revision}[/{path}]]
88 88 --------------------------
89 89
90 90 Show repository or file history.
91 91
92 92 For URLs of the form ``/log/{revision}``, a list of changesets starting at
93 93 the specified changeset identifier is shown. If ``{revision}`` is not
94 94 defined, the default is ``tip``. This form is equivalent to the
95 95 ``changelog`` handler.
96 96
97 97 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
98 98 file will be shown. This form is equivalent to the ``filelog`` handler.
99 99 """
100 100
101 101 if web.req.qsparams.get('file'):
102 102 return filelog(web)
103 103 else:
104 104 return changelog(web)
105 105
106 106 @webcommand('rawfile')
107 107 def rawfile(web):
108 108 guessmime = web.configbool('web', 'guessmime')
109 109
110 110 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
111 111 if not path:
112 112 return manifest(web)
113 113
114 114 try:
115 115 fctx = webutil.filectx(web.repo, web.req)
116 116 except error.LookupError as inst:
117 117 try:
118 118 return manifest(web)
119 119 except ErrorResponse:
120 120 raise inst
121 121
122 122 path = fctx.path()
123 123 text = fctx.data()
124 124 mt = 'application/binary'
125 125 if guessmime:
126 126 mt = mimetypes.guess_type(pycompat.fsdecode(path))[0]
127 127 if mt is None:
128 128 if stringutil.binary(text):
129 129 mt = 'application/binary'
130 130 else:
131 131 mt = 'text/plain'
132 132 else:
133 133 mt = pycompat.sysbytes(mt)
134 134
135 135 if mt.startswith('text/'):
136 136 mt += '; charset="%s"' % encoding.encoding
137 137
138 138 web.res.headers['Content-Type'] = mt
139 139 filename = (path.rpartition('/')[-1]
140 140 .replace('\\', '\\\\').replace('"', '\\"'))
141 141 web.res.headers['Content-Disposition'] = 'inline; filename="%s"' % filename
142 142 web.res.setbodybytes(text)
143 143 return web.res.sendresponse()
144 144
145 145 def _filerevision(web, fctx):
146 146 f = fctx.path()
147 147 text = fctx.data()
148 148 parity = paritygen(web.stripecount)
149 149 ishead = fctx.filenode() in fctx.filelog().heads()
150 150
151 151 if stringutil.binary(text):
152 152 mt = pycompat.sysbytes(
153 153 mimetypes.guess_type(pycompat.fsdecode(f))[0]
154 154 or r'application/octet-stream')
155 155 text = '(binary:%s)' % mt
156 156
157 157 def lines(context):
158 158 for lineno, t in enumerate(text.splitlines(True)):
159 159 yield {"line": t,
160 160 "lineid": "l%d" % (lineno + 1),
161 161 "linenumber": "% 6d" % (lineno + 1),
162 162 "parity": next(parity)}
163 163
164 164 return web.sendtemplate(
165 165 'filerevision',
166 166 file=f,
167 167 path=webutil.up(f),
168 168 text=templateutil.mappinggenerator(lines),
169 169 symrev=webutil.symrevorshortnode(web.req, fctx),
170 170 rename=webutil.renamelink(fctx),
171 171 permissions=fctx.manifest().flags(f),
172 172 ishead=int(ishead),
173 173 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
174 174
175 175 @webcommand('file')
176 176 def file(web):
177 177 """
178 178 /file/{revision}[/{path}]
179 179 -------------------------
180 180
181 181 Show information about a directory or file in the repository.
182 182
183 183 Info about the ``path`` given as a URL parameter will be rendered.
184 184
185 185 If ``path`` is a directory, information about the entries in that
186 186 directory will be rendered. This form is equivalent to the ``manifest``
187 187 handler.
188 188
189 189 If ``path`` is a file, information about that file will be shown via
190 190 the ``filerevision`` template.
191 191
192 192 If ``path`` is not defined, information about the root directory will
193 193 be rendered.
194 194 """
195 195 if web.req.qsparams.get('style') == 'raw':
196 196 return rawfile(web)
197 197
198 198 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
199 199 if not path:
200 200 return manifest(web)
201 201 try:
202 202 return _filerevision(web, webutil.filectx(web.repo, web.req))
203 203 except error.LookupError as inst:
204 204 try:
205 205 return manifest(web)
206 206 except ErrorResponse:
207 207 raise inst
208 208
209 209 def _search(web):
210 210 MODE_REVISION = 'rev'
211 211 MODE_KEYWORD = 'keyword'
212 212 MODE_REVSET = 'revset'
213 213
214 214 def revsearch(ctx):
215 215 yield ctx
216 216
217 217 def keywordsearch(query):
218 218 lower = encoding.lower
219 219 qw = lower(query).split()
220 220
221 221 def revgen():
222 222 cl = web.repo.changelog
223 223 for i in pycompat.xrange(len(web.repo) - 1, 0, -100):
224 224 l = []
225 225 for j in cl.revs(max(0, i - 99), i):
226 226 ctx = web.repo[j]
227 227 l.append(ctx)
228 228 l.reverse()
229 229 for e in l:
230 230 yield e
231 231
232 232 for ctx in revgen():
233 233 miss = 0
234 234 for q in qw:
235 235 if not (q in lower(ctx.user()) or
236 236 q in lower(ctx.description()) or
237 237 q in lower(" ".join(ctx.files()))):
238 238 miss = 1
239 239 break
240 240 if miss:
241 241 continue
242 242
243 243 yield ctx
244 244
245 245 def revsetsearch(revs):
246 246 for r in revs:
247 247 yield web.repo[r]
248 248
249 249 searchfuncs = {
250 250 MODE_REVISION: (revsearch, 'exact revision search'),
251 251 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
252 252 MODE_REVSET: (revsetsearch, 'revset expression search'),
253 253 }
254 254
255 255 def getsearchmode(query):
256 256 try:
257 257 ctx = scmutil.revsymbol(web.repo, query)
258 258 except (error.RepoError, error.LookupError):
259 259 # query is not an exact revision pointer, need to
260 260 # decide if it's a revset expression or keywords
261 261 pass
262 262 else:
263 263 return MODE_REVISION, ctx
264 264
265 265 revdef = 'reverse(%s)' % query
266 266 try:
267 267 tree = revsetlang.parse(revdef)
268 268 except error.ParseError:
269 269 # can't parse to a revset tree
270 270 return MODE_KEYWORD, query
271 271
272 272 if revsetlang.depth(tree) <= 2:
273 273 # no revset syntax used
274 274 return MODE_KEYWORD, query
275 275
276 276 if any((token, (value or '')[:3]) == ('string', 're:')
277 277 for token, value, pos in revsetlang.tokenize(revdef)):
278 278 return MODE_KEYWORD, query
279 279
280 280 funcsused = revsetlang.funcsused(tree)
281 281 if not funcsused.issubset(revset.safesymbols):
282 282 return MODE_KEYWORD, query
283 283
284 284 try:
285 285 mfunc = revset.match(web.repo.ui, revdef,
286 286 lookup=revset.lookupfn(web.repo))
287 287 revs = mfunc(web.repo)
288 288 return MODE_REVSET, revs
289 289 # ParseError: wrongly placed tokens, wrongs arguments, etc
290 290 # RepoLookupError: no such revision, e.g. in 'revision:'
291 291 # Abort: bookmark/tag not exists
292 292 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
293 293 except (error.ParseError, error.RepoLookupError, error.Abort,
294 294 LookupError):
295 295 return MODE_KEYWORD, query
296 296
297 297 def changelist(context):
298 298 count = 0
299 299
300 300 for ctx in searchfunc[0](funcarg):
301 301 count += 1
302 302 n = scmutil.binnode(ctx)
303 303 showtags = webutil.showtag(web.repo, 'changelogtag', n)
304 304 files = webutil.listfilediffs(ctx.files(), n, web.maxfiles)
305 305
306 306 lm = webutil.commonentry(web.repo, ctx)
307 307 lm.update({
308 308 'parity': next(parity),
309 309 'changelogtag': showtags,
310 310 'files': files,
311 311 })
312 312 yield lm
313 313
314 314 if count >= revcount:
315 315 break
316 316
317 317 query = web.req.qsparams['rev']
318 318 revcount = web.maxchanges
319 319 if 'revcount' in web.req.qsparams:
320 320 try:
321 321 revcount = int(web.req.qsparams.get('revcount', revcount))
322 322 revcount = max(revcount, 1)
323 323 web.tmpl.defaults['sessionvars']['revcount'] = revcount
324 324 except ValueError:
325 325 pass
326 326
327 327 lessvars = copy.copy(web.tmpl.defaults['sessionvars'])
328 328 lessvars['revcount'] = max(revcount // 2, 1)
329 329 lessvars['rev'] = query
330 330 morevars = copy.copy(web.tmpl.defaults['sessionvars'])
331 331 morevars['revcount'] = revcount * 2
332 332 morevars['rev'] = query
333 333
334 334 mode, funcarg = getsearchmode(query)
335 335
336 336 if 'forcekw' in web.req.qsparams:
337 337 showforcekw = ''
338 338 showunforcekw = searchfuncs[mode][1]
339 339 mode = MODE_KEYWORD
340 340 funcarg = query
341 341 else:
342 342 if mode != MODE_KEYWORD:
343 343 showforcekw = searchfuncs[MODE_KEYWORD][1]
344 344 else:
345 345 showforcekw = ''
346 346 showunforcekw = ''
347 347
348 348 searchfunc = searchfuncs[mode]
349 349
350 350 tip = web.repo['tip']
351 351 parity = paritygen(web.stripecount)
352 352
353 353 return web.sendtemplate(
354 354 'search',
355 355 query=query,
356 356 node=tip.hex(),
357 357 symrev='tip',
358 358 entries=templateutil.mappinggenerator(changelist, name='searchentry'),
359 359 archives=web.archivelist('tip'),
360 360 morevars=morevars,
361 361 lessvars=lessvars,
362 362 modedesc=searchfunc[1],
363 363 showforcekw=showforcekw,
364 364 showunforcekw=showunforcekw)
365 365
366 366 @webcommand('changelog')
367 367 def changelog(web, shortlog=False):
368 368 """
369 369 /changelog[/{revision}]
370 370 -----------------------
371 371
372 372 Show information about multiple changesets.
373 373
374 374 If the optional ``revision`` URL argument is absent, information about
375 375 all changesets starting at ``tip`` will be rendered. If the ``revision``
376 376 argument is present, changesets will be shown starting from the specified
377 377 revision.
378 378
379 379 If ``revision`` is absent, the ``rev`` query string argument may be
380 380 defined. This will perform a search for changesets.
381 381
382 382 The argument for ``rev`` can be a single revision, a revision set,
383 383 or a literal keyword to search for in changeset data (equivalent to
384 384 :hg:`log -k`).
385 385
386 386 The ``revcount`` query string argument defines the maximum numbers of
387 387 changesets to render.
388 388
389 389 For non-searches, the ``changelog`` template will be rendered.
390 390 """
391 391
392 392 query = ''
393 393 if 'node' in web.req.qsparams:
394 394 ctx = webutil.changectx(web.repo, web.req)
395 395 symrev = webutil.symrevorshortnode(web.req, ctx)
396 396 elif 'rev' in web.req.qsparams:
397 397 return _search(web)
398 398 else:
399 399 ctx = web.repo['tip']
400 400 symrev = 'tip'
401 401
402 402 def changelist(maxcount):
403 403 revs = []
404 404 if pos != -1:
405 405 revs = web.repo.changelog.revs(pos, 0)
406 406
407 407 for entry in webutil.changelistentries(web, revs, maxcount, parity):
408 408 yield entry
409 409
410 410 if shortlog:
411 411 revcount = web.maxshortchanges
412 412 else:
413 413 revcount = web.maxchanges
414 414
415 415 if 'revcount' in web.req.qsparams:
416 416 try:
417 417 revcount = int(web.req.qsparams.get('revcount', revcount))
418 418 revcount = max(revcount, 1)
419 419 web.tmpl.defaults['sessionvars']['revcount'] = revcount
420 420 except ValueError:
421 421 pass
422 422
423 423 lessvars = copy.copy(web.tmpl.defaults['sessionvars'])
424 424 lessvars['revcount'] = max(revcount // 2, 1)
425 425 morevars = copy.copy(web.tmpl.defaults['sessionvars'])
426 426 morevars['revcount'] = revcount * 2
427 427
428 428 count = len(web.repo)
429 429 pos = ctx.rev()
430 430 parity = paritygen(web.stripecount)
431 431
432 432 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
433 433
434 434 entries = list(changelist(revcount + 1))
435 435 latestentry = entries[:1]
436 436 if len(entries) > revcount:
437 437 nextentry = entries[-1:]
438 438 entries = entries[:-1]
439 439 else:
440 440 nextentry = []
441 441
442 442 return web.sendtemplate(
443 443 'shortlog' if shortlog else 'changelog',
444 444 changenav=changenav,
445 445 node=ctx.hex(),
446 446 rev=pos,
447 447 symrev=symrev,
448 448 changesets=count,
449 449 entries=templateutil.mappinglist(entries),
450 450 latestentry=templateutil.mappinglist(latestentry),
451 451 nextentry=templateutil.mappinglist(nextentry),
452 452 archives=web.archivelist('tip'),
453 453 revcount=revcount,
454 454 morevars=morevars,
455 455 lessvars=lessvars,
456 456 query=query)
457 457
458 458 @webcommand('shortlog')
459 459 def shortlog(web):
460 460 """
461 461 /shortlog
462 462 ---------
463 463
464 464 Show basic information about a set of changesets.
465 465
466 466 This accepts the same parameters as the ``changelog`` handler. The only
467 467 difference is the ``shortlog`` template will be rendered instead of the
468 468 ``changelog`` template.
469 469 """
470 470 return changelog(web, shortlog=True)
471 471
472 472 @webcommand('changeset')
473 473 def changeset(web):
474 474 """
475 475 /changeset[/{revision}]
476 476 -----------------------
477 477
478 478 Show information about a single changeset.
479 479
480 480 A URL path argument is the changeset identifier to show. See ``hg help
481 481 revisions`` for possible values. If not defined, the ``tip`` changeset
482 482 will be shown.
483 483
484 484 The ``changeset`` template is rendered. Contents of the ``changesettag``,
485 485 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
486 486 templates related to diffs may all be used to produce the output.
487 487 """
488 488 ctx = webutil.changectx(web.repo, web.req)
489 489
490 490 return web.sendtemplate(
491 491 'changeset',
492 492 **webutil.changesetentry(web, ctx))
493 493
494 494 rev = webcommand('rev')(changeset)
495 495
496 496 def decodepath(path):
497 497 """Hook for mapping a path in the repository to a path in the
498 498 working copy.
499 499
500 500 Extensions (e.g., largefiles) can override this to remap files in
501 501 the virtual file system presented by the manifest command below."""
502 502 return path
503 503
504 504 @webcommand('manifest')
505 505 def manifest(web):
506 506 """
507 507 /manifest[/{revision}[/{path}]]
508 508 -------------------------------
509 509
510 510 Show information about a directory.
511 511
512 512 If the URL path arguments are omitted, information about the root
513 513 directory for the ``tip`` changeset will be shown.
514 514
515 515 Because this handler can only show information for directories, it
516 516 is recommended to use the ``file`` handler instead, as it can handle both
517 517 directories and files.
518 518
519 519 The ``manifest`` template will be rendered for this handler.
520 520 """
521 521 if 'node' in web.req.qsparams:
522 522 ctx = webutil.changectx(web.repo, web.req)
523 523 symrev = webutil.symrevorshortnode(web.req, ctx)
524 524 else:
525 525 ctx = web.repo['tip']
526 526 symrev = 'tip'
527 527 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
528 528 mf = ctx.manifest()
529 529 node = scmutil.binnode(ctx)
530 530
531 531 files = {}
532 532 dirs = {}
533 533 parity = paritygen(web.stripecount)
534 534
535 535 if path and path[-1:] != "/":
536 536 path += "/"
537 537 l = len(path)
538 538 abspath = "/" + path
539 539
540 540 for full, n in mf.iteritems():
541 541 # the virtual path (working copy path) used for the full
542 542 # (repository) path
543 543 f = decodepath(full)
544 544
545 545 if f[:l] != path:
546 546 continue
547 547 remain = f[l:]
548 548 elements = remain.split('/')
549 549 if len(elements) == 1:
550 550 files[remain] = full
551 551 else:
552 552 h = dirs # need to retain ref to dirs (root)
553 553 for elem in elements[0:-1]:
554 554 if elem not in h:
555 555 h[elem] = {}
556 556 h = h[elem]
557 557 if len(h) > 1:
558 558 break
559 559 h[None] = None # denotes files present
560 560
561 561 if mf and not files and not dirs:
562 562 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
563 563
564 564 def filelist(context):
565 565 for f in sorted(files):
566 566 full = files[f]
567 567
568 568 fctx = ctx.filectx(full)
569 569 yield {"file": full,
570 570 "parity": next(parity),
571 571 "basename": f,
572 572 "date": fctx.date(),
573 573 "size": fctx.size(),
574 574 "permissions": mf.flags(full)}
575 575
576 576 def dirlist(context):
577 577 for d in sorted(dirs):
578 578
579 579 emptydirs = []
580 580 h = dirs[d]
581 581 while isinstance(h, dict) and len(h) == 1:
582 582 k, v = next(iter(h.items()))
583 583 if v:
584 584 emptydirs.append(k)
585 585 h = v
586 586
587 587 path = "%s%s" % (abspath, d)
588 588 yield {"parity": next(parity),
589 589 "path": path,
590 590 "emptydirs": "/".join(emptydirs),
591 591 "basename": d}
592 592
593 593 return web.sendtemplate(
594 594 'manifest',
595 595 symrev=symrev,
596 596 path=abspath,
597 597 up=webutil.up(abspath),
598 598 upparity=next(parity),
599 599 fentries=templateutil.mappinggenerator(filelist),
600 600 dentries=templateutil.mappinggenerator(dirlist),
601 601 archives=web.archivelist(hex(node)),
602 602 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
603 603
604 604 @webcommand('tags')
605 605 def tags(web):
606 606 """
607 607 /tags
608 608 -----
609 609
610 610 Show information about tags.
611 611
612 612 No arguments are accepted.
613 613
614 614 The ``tags`` template is rendered.
615 615 """
616 616 i = list(reversed(web.repo.tagslist()))
617 617 parity = paritygen(web.stripecount)
618 618
619 619 def entries(context, notip, latestonly):
620 620 t = i
621 621 if notip:
622 622 t = [(k, n) for k, n in i if k != "tip"]
623 623 if latestonly:
624 624 t = t[:1]
625 625 for k, n in t:
626 626 yield {"parity": next(parity),
627 627 "tag": k,
628 628 "date": web.repo[n].date(),
629 629 "node": hex(n)}
630 630
631 631 return web.sendtemplate(
632 632 'tags',
633 633 node=hex(web.repo.changelog.tip()),
634 634 entries=templateutil.mappinggenerator(entries, args=(False, False)),
635 635 entriesnotip=templateutil.mappinggenerator(entries,
636 636 args=(True, False)),
637 637 latestentry=templateutil.mappinggenerator(entries, args=(True, True)))
638 638
639 639 @webcommand('bookmarks')
640 640 def bookmarks(web):
641 641 """
642 642 /bookmarks
643 643 ----------
644 644
645 645 Show information about bookmarks.
646 646
647 647 No arguments are accepted.
648 648
649 649 The ``bookmarks`` template is rendered.
650 650 """
651 651 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
652 652 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
653 653 i = sorted(i, key=sortkey, reverse=True)
654 654 parity = paritygen(web.stripecount)
655 655
656 656 def entries(context, latestonly):
657 657 t = i
658 658 if latestonly:
659 659 t = i[:1]
660 660 for k, n in t:
661 661 yield {"parity": next(parity),
662 662 "bookmark": k,
663 663 "date": web.repo[n].date(),
664 664 "node": hex(n)}
665 665
666 666 if i:
667 667 latestrev = i[0][1]
668 668 else:
669 669 latestrev = -1
670 670 lastdate = web.repo[latestrev].date()
671 671
672 672 return web.sendtemplate(
673 673 'bookmarks',
674 674 node=hex(web.repo.changelog.tip()),
675 675 lastchange=templateutil.mappinglist([{'date': lastdate}]),
676 676 entries=templateutil.mappinggenerator(entries, args=(False,)),
677 677 latestentry=templateutil.mappinggenerator(entries, args=(True,)))
678 678
679 679 @webcommand('branches')
680 680 def branches(web):
681 681 """
682 682 /branches
683 683 ---------
684 684
685 685 Show information about branches.
686 686
687 687 All known branches are contained in the output, even closed branches.
688 688
689 689 No arguments are accepted.
690 690
691 691 The ``branches`` template is rendered.
692 692 """
693 693 entries = webutil.branchentries(web.repo, web.stripecount)
694 694 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
695 695
696 696 return web.sendtemplate(
697 697 'branches',
698 698 node=hex(web.repo.changelog.tip()),
699 699 entries=entries,
700 700 latestentry=latestentry)
701 701
702 702 @webcommand('summary')
703 703 def summary(web):
704 704 """
705 705 /summary
706 706 --------
707 707
708 708 Show a summary of repository state.
709 709
710 710 Information about the latest changesets, bookmarks, tags, and branches
711 711 is captured by this handler.
712 712
713 713 The ``summary`` template is rendered.
714 714 """
715 715 i = reversed(web.repo.tagslist())
716 716
717 717 def tagentries(context):
718 718 parity = paritygen(web.stripecount)
719 719 count = 0
720 720 for k, n in i:
721 721 if k == "tip": # skip tip
722 722 continue
723 723
724 724 count += 1
725 725 if count > 10: # limit to 10 tags
726 726 break
727 727
728 728 yield {
729 729 'parity': next(parity),
730 730 'tag': k,
731 731 'node': hex(n),
732 732 'date': web.repo[n].date(),
733 733 }
734 734
735 735 def bookmarks(context):
736 736 parity = paritygen(web.stripecount)
737 737 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
738 738 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
739 739 marks = sorted(marks, key=sortkey, reverse=True)
740 740 for k, n in marks[:10]: # limit to 10 bookmarks
741 741 yield {'parity': next(parity),
742 742 'bookmark': k,
743 743 'date': web.repo[n].date(),
744 744 'node': hex(n)}
745 745
746 746 def changelist(context):
747 747 parity = paritygen(web.stripecount, offset=start - end)
748 748 l = [] # build a list in forward order for efficiency
749 749 revs = []
750 750 if start < end:
751 751 revs = web.repo.changelog.revs(start, end - 1)
752 752 for i in revs:
753 753 ctx = web.repo[i]
754 754 lm = webutil.commonentry(web.repo, ctx)
755 755 lm['parity'] = next(parity)
756 756 l.append(lm)
757 757
758 758 for entry in reversed(l):
759 759 yield entry
760 760
761 761 tip = web.repo['tip']
762 762 count = len(web.repo)
763 763 start = max(0, count - web.maxchanges)
764 764 end = min(count, start + web.maxchanges)
765 765
766 766 desc = web.config("web", "description")
767 767 if not desc:
768 768 desc = 'unknown'
769 769 labels = web.configlist('web', 'labels')
770 770
771 771 return web.sendtemplate(
772 772 'summary',
773 773 desc=desc,
774 774 owner=get_contact(web.config) or 'unknown',
775 775 lastchange=tip.date(),
776 776 tags=templateutil.mappinggenerator(tagentries, name='tagentry'),
777 777 bookmarks=templateutil.mappinggenerator(bookmarks),
778 778 branches=webutil.branchentries(web.repo, web.stripecount, 10),
779 779 shortlog=templateutil.mappinggenerator(changelist,
780 780 name='shortlogentry'),
781 781 node=tip.hex(),
782 782 symrev='tip',
783 783 archives=web.archivelist('tip'),
784 784 labels=templateutil.hybridlist(labels, name='label'))
785 785
786 786 @webcommand('filediff')
787 787 def filediff(web):
788 788 """
789 789 /diff/{revision}/{path}
790 790 -----------------------
791 791
792 792 Show how a file changed in a particular commit.
793 793
794 794 The ``filediff`` template is rendered.
795 795
796 796 This handler is registered under both the ``/diff`` and ``/filediff``
797 797 paths. ``/diff`` is used in modern code.
798 798 """
799 799 fctx, ctx = None, None
800 800 try:
801 801 fctx = webutil.filectx(web.repo, web.req)
802 802 except LookupError:
803 803 ctx = webutil.changectx(web.repo, web.req)
804 804 path = webutil.cleanpath(web.repo, web.req.qsparams['file'])
805 805 if path not in ctx.files():
806 806 raise
807 807
808 808 if fctx is not None:
809 809 path = fctx.path()
810 810 ctx = fctx.changectx()
811 811 basectx = ctx.p1()
812 812
813 813 style = web.config('web', 'style')
814 814 if 'style' in web.req.qsparams:
815 815 style = web.req.qsparams['style']
816 816
817 817 diffs = webutil.diffs(web, ctx, basectx, [path], style)
818 818 if fctx is not None:
819 819 rename = webutil.renamelink(fctx)
820 820 ctx = fctx
821 821 else:
822 822 rename = templateutil.mappinglist([])
823 823 ctx = ctx
824 824
825 825 return web.sendtemplate(
826 826 'filediff',
827 827 file=path,
828 828 symrev=webutil.symrevorshortnode(web.req, ctx),
829 829 rename=rename,
830 830 diff=diffs,
831 831 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
832 832
833 833 diff = webcommand('diff')(filediff)
834 834
835 835 @webcommand('comparison')
836 836 def comparison(web):
837 837 """
838 838 /comparison/{revision}/{path}
839 839 -----------------------------
840 840
841 841 Show a comparison between the old and new versions of a file from changes
842 842 made on a particular revision.
843 843
844 844 This is similar to the ``diff`` handler. However, this form features
845 845 a split or side-by-side diff rather than a unified diff.
846 846
847 847 The ``context`` query string argument can be used to control the lines of
848 848 context in the diff.
849 849
850 850 The ``filecomparison`` template is rendered.
851 851 """
852 852 ctx = webutil.changectx(web.repo, web.req)
853 853 if 'file' not in web.req.qsparams:
854 854 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
855 855 path = webutil.cleanpath(web.repo, web.req.qsparams['file'])
856 856
857 857 parsecontext = lambda v: v == 'full' and -1 or int(v)
858 858 if 'context' in web.req.qsparams:
859 859 context = parsecontext(web.req.qsparams['context'])
860 860 else:
861 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
861 context = parsecontext(web.config('web', 'comparisoncontext'))
862 862
863 863 def filelines(f):
864 864 if f.isbinary():
865 865 mt = pycompat.sysbytes(
866 866 mimetypes.guess_type(pycompat.fsdecode(f.path()))[0]
867 867 or r'application/octet-stream')
868 868 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
869 869 return f.data().splitlines()
870 870
871 871 fctx = None
872 872 parent = ctx.p1()
873 873 leftrev = parent.rev()
874 874 leftnode = parent.node()
875 875 rightrev = ctx.rev()
876 876 rightnode = scmutil.binnode(ctx)
877 877 if path in ctx:
878 878 fctx = ctx[path]
879 879 rightlines = filelines(fctx)
880 880 if path not in parent:
881 881 leftlines = ()
882 882 else:
883 883 pfctx = parent[path]
884 884 leftlines = filelines(pfctx)
885 885 else:
886 886 rightlines = ()
887 887 pfctx = ctx.parents()[0][path]
888 888 leftlines = filelines(pfctx)
889 889
890 890 comparison = webutil.compare(context, leftlines, rightlines)
891 891 if fctx is not None:
892 892 rename = webutil.renamelink(fctx)
893 893 ctx = fctx
894 894 else:
895 895 rename = templateutil.mappinglist([])
896 896 ctx = ctx
897 897
898 898 return web.sendtemplate(
899 899 'filecomparison',
900 900 file=path,
901 901 symrev=webutil.symrevorshortnode(web.req, ctx),
902 902 rename=rename,
903 903 leftrev=leftrev,
904 904 leftnode=hex(leftnode),
905 905 rightrev=rightrev,
906 906 rightnode=hex(rightnode),
907 907 comparison=comparison,
908 908 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
909 909
910 910 @webcommand('annotate')
911 911 def annotate(web):
912 912 """
913 913 /annotate/{revision}/{path}
914 914 ---------------------------
915 915
916 916 Show changeset information for each line in a file.
917 917
918 918 The ``ignorews``, ``ignorewsamount``, ``ignorewseol``, and
919 919 ``ignoreblanklines`` query string arguments have the same meaning as
920 920 their ``[annotate]`` config equivalents. It uses the hgrc boolean
921 921 parsing logic to interpret the value. e.g. ``0`` and ``false`` are
922 922 false and ``1`` and ``true`` are true. If not defined, the server
923 923 default settings are used.
924 924
925 925 The ``fileannotate`` template is rendered.
926 926 """
927 927 fctx = webutil.filectx(web.repo, web.req)
928 928 f = fctx.path()
929 929 parity = paritygen(web.stripecount)
930 930 ishead = fctx.filenode() in fctx.filelog().heads()
931 931
932 932 # parents() is called once per line and several lines likely belong to
933 933 # same revision. So it is worth caching.
934 934 # TODO there are still redundant operations within basefilectx.parents()
935 935 # and from the fctx.annotate() call itself that could be cached.
936 936 parentscache = {}
937 937 def parents(context, f):
938 938 rev = f.rev()
939 939 if rev not in parentscache:
940 940 parentscache[rev] = []
941 941 for p in f.parents():
942 942 entry = {
943 943 'node': p.hex(),
944 944 'rev': p.rev(),
945 945 }
946 946 parentscache[rev].append(entry)
947 947
948 948 for p in parentscache[rev]:
949 949 yield p
950 950
951 951 def annotate(context):
952 952 if fctx.isbinary():
953 953 mt = pycompat.sysbytes(
954 954 mimetypes.guess_type(pycompat.fsdecode(fctx.path()))[0]
955 955 or r'application/octet-stream')
956 956 lines = [dagop.annotateline(fctx=fctx.filectx(fctx.filerev()),
957 957 lineno=1, text='(binary:%s)' % mt)]
958 958 else:
959 959 lines = webutil.annotate(web.req, fctx, web.repo.ui)
960 960
961 961 previousrev = None
962 962 blockparitygen = paritygen(1)
963 963 for lineno, aline in enumerate(lines):
964 964 f = aline.fctx
965 965 rev = f.rev()
966 966 if rev != previousrev:
967 967 blockhead = True
968 968 blockparity = next(blockparitygen)
969 969 else:
970 970 blockhead = None
971 971 previousrev = rev
972 972 yield {"parity": next(parity),
973 973 "node": f.hex(),
974 974 "rev": rev,
975 975 "author": f.user(),
976 976 "parents": templateutil.mappinggenerator(parents, args=(f,)),
977 977 "desc": f.description(),
978 978 "extra": f.extra(),
979 979 "file": f.path(),
980 980 "blockhead": blockhead,
981 981 "blockparity": blockparity,
982 982 "targetline": aline.lineno,
983 983 "line": aline.text,
984 984 "lineno": lineno + 1,
985 985 "lineid": "l%d" % (lineno + 1),
986 986 "linenumber": "% 6d" % (lineno + 1),
987 987 "revdate": f.date()}
988 988
989 989 diffopts = webutil.difffeatureopts(web.req, web.repo.ui, 'annotate')
990 990 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults}
991 991
992 992 return web.sendtemplate(
993 993 'fileannotate',
994 994 file=f,
995 995 annotate=templateutil.mappinggenerator(annotate),
996 996 path=webutil.up(f),
997 997 symrev=webutil.symrevorshortnode(web.req, fctx),
998 998 rename=webutil.renamelink(fctx),
999 999 permissions=fctx.manifest().flags(f),
1000 1000 ishead=int(ishead),
1001 1001 diffopts=templateutil.hybriddict(diffopts),
1002 1002 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
1003 1003
1004 1004 @webcommand('filelog')
1005 1005 def filelog(web):
1006 1006 """
1007 1007 /filelog/{revision}/{path}
1008 1008 --------------------------
1009 1009
1010 1010 Show information about the history of a file in the repository.
1011 1011
1012 1012 The ``revcount`` query string argument can be defined to control the
1013 1013 maximum number of entries to show.
1014 1014
1015 1015 The ``filelog`` template will be rendered.
1016 1016 """
1017 1017
1018 1018 try:
1019 1019 fctx = webutil.filectx(web.repo, web.req)
1020 1020 f = fctx.path()
1021 1021 fl = fctx.filelog()
1022 1022 except error.LookupError:
1023 1023 f = webutil.cleanpath(web.repo, web.req.qsparams['file'])
1024 1024 fl = web.repo.file(f)
1025 1025 numrevs = len(fl)
1026 1026 if not numrevs: # file doesn't exist at all
1027 1027 raise
1028 1028 rev = webutil.changectx(web.repo, web.req).rev()
1029 1029 first = fl.linkrev(0)
1030 1030 if rev < first: # current rev is from before file existed
1031 1031 raise
1032 1032 frev = numrevs - 1
1033 1033 while fl.linkrev(frev) > rev:
1034 1034 frev -= 1
1035 1035 fctx = web.repo.filectx(f, fl.linkrev(frev))
1036 1036
1037 1037 revcount = web.maxshortchanges
1038 1038 if 'revcount' in web.req.qsparams:
1039 1039 try:
1040 1040 revcount = int(web.req.qsparams.get('revcount', revcount))
1041 1041 revcount = max(revcount, 1)
1042 1042 web.tmpl.defaults['sessionvars']['revcount'] = revcount
1043 1043 except ValueError:
1044 1044 pass
1045 1045
1046 1046 lrange = webutil.linerange(web.req)
1047 1047
1048 1048 lessvars = copy.copy(web.tmpl.defaults['sessionvars'])
1049 1049 lessvars['revcount'] = max(revcount // 2, 1)
1050 1050 morevars = copy.copy(web.tmpl.defaults['sessionvars'])
1051 1051 morevars['revcount'] = revcount * 2
1052 1052
1053 1053 patch = 'patch' in web.req.qsparams
1054 1054 if patch:
1055 1055 lessvars['patch'] = morevars['patch'] = web.req.qsparams['patch']
1056 1056 descend = 'descend' in web.req.qsparams
1057 1057 if descend:
1058 1058 lessvars['descend'] = morevars['descend'] = web.req.qsparams['descend']
1059 1059
1060 1060 count = fctx.filerev() + 1
1061 1061 start = max(0, count - revcount) # first rev on this page
1062 1062 end = min(count, start + revcount) # last rev on this page
1063 1063 parity = paritygen(web.stripecount, offset=start - end)
1064 1064
1065 1065 repo = web.repo
1066 1066 filelog = fctx.filelog()
1067 1067 revs = [filerev for filerev in filelog.revs(start, end - 1)
1068 1068 if filelog.linkrev(filerev) in repo]
1069 1069 entries = []
1070 1070
1071 1071 diffstyle = web.config('web', 'style')
1072 1072 if 'style' in web.req.qsparams:
1073 1073 diffstyle = web.req.qsparams['style']
1074 1074
1075 1075 def diff(fctx, linerange=None):
1076 1076 ctx = fctx.changectx()
1077 1077 basectx = ctx.p1()
1078 1078 path = fctx.path()
1079 1079 return webutil.diffs(web, ctx, basectx, [path], diffstyle,
1080 1080 linerange=linerange,
1081 1081 lineidprefix='%s-' % ctx.hex()[:12])
1082 1082
1083 1083 linerange = None
1084 1084 if lrange is not None:
1085 1085 linerange = webutil.formatlinerange(*lrange)
1086 1086 # deactivate numeric nav links when linerange is specified as this
1087 1087 # would required a dedicated "revnav" class
1088 1088 nav = templateutil.mappinglist([])
1089 1089 if descend:
1090 1090 it = dagop.blockdescendants(fctx, *lrange)
1091 1091 else:
1092 1092 it = dagop.blockancestors(fctx, *lrange)
1093 1093 for i, (c, lr) in enumerate(it, 1):
1094 1094 diffs = None
1095 1095 if patch:
1096 1096 diffs = diff(c, linerange=lr)
1097 1097 # follow renames accross filtered (not in range) revisions
1098 1098 path = c.path()
1099 1099 lm = webutil.commonentry(repo, c)
1100 1100 lm.update({
1101 1101 'parity': next(parity),
1102 1102 'filerev': c.rev(),
1103 1103 'file': path,
1104 1104 'diff': diffs,
1105 1105 'linerange': webutil.formatlinerange(*lr),
1106 1106 'rename': templateutil.mappinglist([]),
1107 1107 })
1108 1108 entries.append(lm)
1109 1109 if i == revcount:
1110 1110 break
1111 1111 lessvars['linerange'] = webutil.formatlinerange(*lrange)
1112 1112 morevars['linerange'] = lessvars['linerange']
1113 1113 else:
1114 1114 for i in revs:
1115 1115 iterfctx = fctx.filectx(i)
1116 1116 diffs = None
1117 1117 if patch:
1118 1118 diffs = diff(iterfctx)
1119 1119 lm = webutil.commonentry(repo, iterfctx)
1120 1120 lm.update({
1121 1121 'parity': next(parity),
1122 1122 'filerev': i,
1123 1123 'file': f,
1124 1124 'diff': diffs,
1125 1125 'rename': webutil.renamelink(iterfctx),
1126 1126 })
1127 1127 entries.append(lm)
1128 1128 entries.reverse()
1129 1129 revnav = webutil.filerevnav(web.repo, fctx.path())
1130 1130 nav = revnav.gen(end - 1, revcount, count)
1131 1131
1132 1132 latestentry = entries[:1]
1133 1133
1134 1134 return web.sendtemplate(
1135 1135 'filelog',
1136 1136 file=f,
1137 1137 nav=nav,
1138 1138 symrev=webutil.symrevorshortnode(web.req, fctx),
1139 1139 entries=templateutil.mappinglist(entries),
1140 1140 descend=descend,
1141 1141 patch=patch,
1142 1142 latestentry=templateutil.mappinglist(latestentry),
1143 1143 linerange=linerange,
1144 1144 revcount=revcount,
1145 1145 morevars=morevars,
1146 1146 lessvars=lessvars,
1147 1147 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
1148 1148
1149 1149 @webcommand('archive')
1150 1150 def archive(web):
1151 1151 """
1152 1152 /archive/{revision}.{format}[/{path}]
1153 1153 -------------------------------------
1154 1154
1155 1155 Obtain an archive of repository content.
1156 1156
1157 1157 The content and type of the archive is defined by a URL path parameter.
1158 1158 ``format`` is the file extension of the archive type to be generated. e.g.
1159 1159 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1160 1160 server configuration.
1161 1161
1162 1162 The optional ``path`` URL parameter controls content to include in the
1163 1163 archive. If omitted, every file in the specified revision is present in the
1164 1164 archive. If included, only the specified file or contents of the specified
1165 1165 directory will be included in the archive.
1166 1166
1167 1167 No template is used for this handler. Raw, binary content is generated.
1168 1168 """
1169 1169
1170 1170 type_ = web.req.qsparams.get('type')
1171 1171 allowed = web.configlist("web", "allow-archive")
1172 1172 key = web.req.qsparams['node']
1173 1173
1174 1174 if type_ not in webutil.archivespecs:
1175 1175 msg = 'Unsupported archive type: %s' % stringutil.pprint(type_)
1176 1176 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1177 1177
1178 1178 if not ((type_ in allowed or
1179 1179 web.configbool("web", "allow" + type_))):
1180 1180 msg = 'Archive type not allowed: %s' % type_
1181 1181 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1182 1182
1183 1183 reponame = re.sub(br"\W+", "-", os.path.basename(web.reponame))
1184 1184 cnode = web.repo.lookup(key)
1185 1185 arch_version = key
1186 1186 if cnode == key or key == 'tip':
1187 1187 arch_version = short(cnode)
1188 1188 name = "%s-%s" % (reponame, arch_version)
1189 1189
1190 1190 ctx = webutil.changectx(web.repo, web.req)
1191 1191 pats = []
1192 1192 match = scmutil.match(ctx, [])
1193 1193 file = web.req.qsparams.get('file')
1194 1194 if file:
1195 1195 pats = ['path:' + file]
1196 1196 match = scmutil.match(ctx, pats, default='path')
1197 1197 if pats:
1198 1198 files = [f for f in ctx.manifest().keys() if match(f)]
1199 1199 if not files:
1200 1200 raise ErrorResponse(HTTP_NOT_FOUND,
1201 1201 'file(s) not found: %s' % file)
1202 1202
1203 1203 mimetype, artype, extension, encoding = webutil.archivespecs[type_]
1204 1204
1205 1205 web.res.headers['Content-Type'] = mimetype
1206 1206 web.res.headers['Content-Disposition'] = 'attachment; filename=%s%s' % (
1207 1207 name, extension)
1208 1208
1209 1209 if encoding:
1210 1210 web.res.headers['Content-Encoding'] = encoding
1211 1211
1212 1212 web.res.setbodywillwrite()
1213 1213 if list(web.res.sendresponse()):
1214 1214 raise error.ProgrammingError('sendresponse() should not emit data '
1215 1215 'if writing later')
1216 1216
1217 1217 bodyfh = web.res.getbodyfile()
1218 1218
1219 1219 archival.archive(web.repo, bodyfh, cnode, artype, prefix=name, match=match,
1220 1220 subrepos=web.configbool("web", "archivesubrepos"))
1221 1221
1222 1222 return []
1223 1223
1224 1224 @webcommand('static')
1225 1225 def static(web):
1226 1226 fname = web.req.qsparams['file']
1227 1227 # a repo owner may set web.static in .hg/hgrc to get any file
1228 1228 # readable by the user running the CGI script
1229 1229 static = web.config("web", "static", untrusted=False)
1230 1230 if not static:
1231 1231 tp = web.templatepath or templater.templatepaths()
1232 1232 if isinstance(tp, str):
1233 1233 tp = [tp]
1234 1234 static = [os.path.join(p, 'static') for p in tp]
1235 1235
1236 1236 staticfile(static, fname, web.res)
1237 1237 return web.res.sendresponse()
1238 1238
1239 1239 @webcommand('graph')
1240 1240 def graph(web):
1241 1241 """
1242 1242 /graph[/{revision}]
1243 1243 -------------------
1244 1244
1245 1245 Show information about the graphical topology of the repository.
1246 1246
1247 1247 Information rendered by this handler can be used to create visual
1248 1248 representations of repository topology.
1249 1249
1250 1250 The ``revision`` URL parameter controls the starting changeset. If it's
1251 1251 absent, the default is ``tip``.
1252 1252
1253 1253 The ``revcount`` query string argument can define the number of changesets
1254 1254 to show information for.
1255 1255
1256 1256 The ``graphtop`` query string argument can specify the starting changeset
1257 1257 for producing ``jsdata`` variable that is used for rendering graph in
1258 1258 JavaScript. By default it has the same value as ``revision``.
1259 1259
1260 1260 This handler will render the ``graph`` template.
1261 1261 """
1262 1262
1263 1263 if 'node' in web.req.qsparams:
1264 1264 ctx = webutil.changectx(web.repo, web.req)
1265 1265 symrev = webutil.symrevorshortnode(web.req, ctx)
1266 1266 else:
1267 1267 ctx = web.repo['tip']
1268 1268 symrev = 'tip'
1269 1269 rev = ctx.rev()
1270 1270
1271 1271 bg_height = 39
1272 1272 revcount = web.maxshortchanges
1273 1273 if 'revcount' in web.req.qsparams:
1274 1274 try:
1275 1275 revcount = int(web.req.qsparams.get('revcount', revcount))
1276 1276 revcount = max(revcount, 1)
1277 1277 web.tmpl.defaults['sessionvars']['revcount'] = revcount
1278 1278 except ValueError:
1279 1279 pass
1280 1280
1281 1281 lessvars = copy.copy(web.tmpl.defaults['sessionvars'])
1282 1282 lessvars['revcount'] = max(revcount // 2, 1)
1283 1283 morevars = copy.copy(web.tmpl.defaults['sessionvars'])
1284 1284 morevars['revcount'] = revcount * 2
1285 1285
1286 1286 graphtop = web.req.qsparams.get('graphtop', ctx.hex())
1287 1287 graphvars = copy.copy(web.tmpl.defaults['sessionvars'])
1288 1288 graphvars['graphtop'] = graphtop
1289 1289
1290 1290 count = len(web.repo)
1291 1291 pos = rev
1292 1292
1293 1293 uprev = min(max(0, count - 1), rev + revcount)
1294 1294 downrev = max(0, rev - revcount)
1295 1295 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1296 1296
1297 1297 tree = []
1298 1298 nextentry = []
1299 1299 lastrev = 0
1300 1300 if pos != -1:
1301 1301 allrevs = web.repo.changelog.revs(pos, 0)
1302 1302 revs = []
1303 1303 for i in allrevs:
1304 1304 revs.append(i)
1305 1305 if len(revs) >= revcount + 1:
1306 1306 break
1307 1307
1308 1308 if len(revs) > revcount:
1309 1309 nextentry = [webutil.commonentry(web.repo, web.repo[revs[-1]])]
1310 1310 revs = revs[:-1]
1311 1311
1312 1312 lastrev = revs[-1]
1313 1313
1314 1314 # We have to feed a baseset to dagwalker as it is expecting smartset
1315 1315 # object. This does not have a big impact on hgweb performance itself
1316 1316 # since hgweb graphing code is not itself lazy yet.
1317 1317 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1318 1318 # As we said one line above... not lazy.
1319 1319 tree = list(item for item in graphmod.colored(dag, web.repo)
1320 1320 if item[1] == graphmod.CHANGESET)
1321 1321
1322 1322 def fulltree():
1323 1323 pos = web.repo[graphtop].rev()
1324 1324 tree = []
1325 1325 if pos != -1:
1326 1326 revs = web.repo.changelog.revs(pos, lastrev)
1327 1327 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1328 1328 tree = list(item for item in graphmod.colored(dag, web.repo)
1329 1329 if item[1] == graphmod.CHANGESET)
1330 1330 return tree
1331 1331
1332 1332 def jsdata(context):
1333 1333 for (id, type, ctx, vtx, edges) in fulltree():
1334 1334 yield {'node': pycompat.bytestr(ctx),
1335 1335 'graphnode': webutil.getgraphnode(web.repo, ctx),
1336 1336 'vertex': vtx,
1337 1337 'edges': edges}
1338 1338
1339 1339 def nodes(context):
1340 1340 parity = paritygen(web.stripecount)
1341 1341 for row, (id, type, ctx, vtx, edges) in enumerate(tree):
1342 1342 entry = webutil.commonentry(web.repo, ctx)
1343 1343 edgedata = [{'col': edge[0],
1344 1344 'nextcol': edge[1],
1345 1345 'color': (edge[2] - 1) % 6 + 1,
1346 1346 'width': edge[3],
1347 1347 'bcolor': edge[4]}
1348 1348 for edge in edges]
1349 1349
1350 1350 entry.update({'col': vtx[0],
1351 1351 'color': (vtx[1] - 1) % 6 + 1,
1352 1352 'parity': next(parity),
1353 1353 'edges': templateutil.mappinglist(edgedata),
1354 1354 'row': row,
1355 1355 'nextrow': row + 1})
1356 1356
1357 1357 yield entry
1358 1358
1359 1359 rows = len(tree)
1360 1360
1361 1361 return web.sendtemplate(
1362 1362 'graph',
1363 1363 rev=rev,
1364 1364 symrev=symrev,
1365 1365 revcount=revcount,
1366 1366 uprev=uprev,
1367 1367 lessvars=lessvars,
1368 1368 morevars=morevars,
1369 1369 downrev=downrev,
1370 1370 graphvars=graphvars,
1371 1371 rows=rows,
1372 1372 bg_height=bg_height,
1373 1373 changesets=count,
1374 1374 nextentry=templateutil.mappinglist(nextentry),
1375 1375 jsdata=templateutil.mappinggenerator(jsdata),
1376 1376 nodes=templateutil.mappinggenerator(nodes),
1377 1377 node=ctx.hex(),
1378 1378 archives=web.archivelist('tip'),
1379 1379 changenav=changenav)
1380 1380
1381 1381 def _getdoc(e):
1382 1382 doc = e[0].__doc__
1383 1383 if doc:
1384 1384 doc = _(doc).partition('\n')[0]
1385 1385 else:
1386 1386 doc = _('(no help text available)')
1387 1387 return doc
1388 1388
1389 1389 @webcommand('help')
1390 1390 def help(web):
1391 1391 """
1392 1392 /help[/{topic}]
1393 1393 ---------------
1394 1394
1395 1395 Render help documentation.
1396 1396
1397 1397 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1398 1398 is defined, that help topic will be rendered. If not, an index of
1399 1399 available help topics will be rendered.
1400 1400
1401 1401 The ``help`` template will be rendered when requesting help for a topic.
1402 1402 ``helptopics`` will be rendered for the index of help topics.
1403 1403 """
1404 1404 from .. import commands, help as helpmod # avoid cycle
1405 1405
1406 1406 topicname = web.req.qsparams.get('node')
1407 1407 if not topicname:
1408 1408 def topics(context):
1409 1409 for h in helpmod.helptable:
1410 1410 entries, summary, _doc = h[0:3]
1411 1411 yield {'topic': entries[0], 'summary': summary}
1412 1412
1413 1413 early, other = [], []
1414 1414 primary = lambda s: s.partition('|')[0]
1415 1415 for c, e in commands.table.iteritems():
1416 1416 doc = _getdoc(e)
1417 1417 if 'DEPRECATED' in doc or c.startswith('debug'):
1418 1418 continue
1419 1419 cmd = primary(c)
1420 1420 if getattr(e[0], 'helpbasic', False):
1421 1421 early.append((cmd, doc))
1422 1422 else:
1423 1423 other.append((cmd, doc))
1424 1424
1425 1425 early.sort()
1426 1426 other.sort()
1427 1427
1428 1428 def earlycommands(context):
1429 1429 for c, doc in early:
1430 1430 yield {'topic': c, 'summary': doc}
1431 1431
1432 1432 def othercommands(context):
1433 1433 for c, doc in other:
1434 1434 yield {'topic': c, 'summary': doc}
1435 1435
1436 1436 return web.sendtemplate(
1437 1437 'helptopics',
1438 1438 topics=templateutil.mappinggenerator(topics),
1439 1439 earlycommands=templateutil.mappinggenerator(earlycommands),
1440 1440 othercommands=templateutil.mappinggenerator(othercommands),
1441 1441 title='Index')
1442 1442
1443 1443 # Render an index of sub-topics.
1444 1444 if topicname in helpmod.subtopics:
1445 1445 topics = []
1446 1446 for entries, summary, _doc in helpmod.subtopics[topicname]:
1447 1447 topics.append({
1448 1448 'topic': '%s.%s' % (topicname, entries[0]),
1449 1449 'basename': entries[0],
1450 1450 'summary': summary,
1451 1451 })
1452 1452
1453 1453 return web.sendtemplate(
1454 1454 'helptopics',
1455 1455 topics=templateutil.mappinglist(topics),
1456 1456 title=topicname,
1457 1457 subindex=True)
1458 1458
1459 1459 u = webutil.wsgiui.load()
1460 1460 u.verbose = True
1461 1461
1462 1462 # Render a page from a sub-topic.
1463 1463 if '.' in topicname:
1464 1464 # TODO implement support for rendering sections, like
1465 1465 # `hg help` works.
1466 1466 topic, subtopic = topicname.split('.', 1)
1467 1467 if topic not in helpmod.subtopics:
1468 1468 raise ErrorResponse(HTTP_NOT_FOUND)
1469 1469 else:
1470 1470 topic = topicname
1471 1471 subtopic = None
1472 1472
1473 1473 try:
1474 1474 doc = helpmod.help_(u, commands, topic, subtopic=subtopic)
1475 1475 except error.Abort:
1476 1476 raise ErrorResponse(HTTP_NOT_FOUND)
1477 1477
1478 1478 return web.sendtemplate(
1479 1479 'help',
1480 1480 topic=topicname,
1481 1481 doc=doc)
1482 1482
1483 1483 # tell hggettext to extract docstrings from these functions:
1484 1484 i18nfunctions = commands.values()
@@ -1,937 +1,937 b''
1 1 #require serve zstd
2 2
3 3 Client version is embedded in HTTP request and is effectively dynamic. Pin the
4 4 version so behavior is deterministic.
5 5
6 6 $ cat > fakeversion.py << EOF
7 7 > from mercurial import util
8 8 > util.version = lambda: '4.2'
9 9 > EOF
10 10
11 11 $ cat >> $HGRCPATH << EOF
12 12 > [extensions]
13 13 > fakeversion = `pwd`/fakeversion.py
14 14 > [format]
15 15 > sparse-revlog = no
16 16 > [devel]
17 17 > legacy.exchange = phases
18 18 > EOF
19 19
20 20 $ hg init server0
21 21 $ cd server0
22 22 $ touch foo
23 23 $ hg -q commit -A -m initial
24 24
25 25 Also disable compression because zstd is optional and causes output to vary
26 26 and because debugging partial responses is hard when compression is involved
27 27
28 28 $ cat > .hg/hgrc << EOF
29 29 > [extensions]
30 30 > badserver = $TESTDIR/badserverext.py
31 31 > [server]
32 32 > compressionengines = none
33 33 > EOF
34 34
35 35 Failure to accept() socket should result in connection related error message
36 36
37 37 $ hg serve --config badserver.closebeforeaccept=true -p $HGPORT -d --pid-file=hg.pid
38 38 $ cat hg.pid > $DAEMON_PIDS
39 39
40 40 $ hg clone http://localhost:$HGPORT/ clone
41 41 abort: error: $ECONNRESET$ (?)
42 42 abort: error: $EADDRNOTAVAIL$ (?)
43 43 [255]
44 44
45 45 (The server exits on its own, but there is a race between that and starting a new server.
46 46 So ensure the process is dead.)
47 47
48 48 $ killdaemons.py $DAEMON_PIDS
49 49
50 50 Failure immediately after accept() should yield connection related error message
51 51
52 52 $ hg serve --config badserver.closeafteraccept=true -p $HGPORT -d --pid-file=hg.pid
53 53 $ cat hg.pid > $DAEMON_PIDS
54 54
55 55 TODO: this usually outputs good results, but sometimes emits abort:
56 56 error: '' on FreeBSD and OS X.
57 57 What we ideally want are:
58 58
59 59 abort: error: $ECONNRESET$
60 60
61 61 The flakiness in this output was observable easily with
62 62 --runs-per-test=20 on macOS 10.12 during the freeze for 4.2.
63 63 $ hg clone http://localhost:$HGPORT/ clone
64 64 abort: error: * (glob)
65 65 [255]
66 66
67 67 $ killdaemons.py $DAEMON_PIDS
68 68
69 69 Failure to read all bytes in initial HTTP request should yield connection related error message
70 70
71 71 $ hg serve --config badserver.closeafterrecvbytes=1 -p $HGPORT -d --pid-file=hg.pid -E error.log
72 72 $ cat hg.pid > $DAEMON_PIDS
73 73
74 74 $ hg clone http://localhost:$HGPORT/ clone
75 abort: error: bad HTTP status line: ''
75 abort: error: bad HTTP status line: * (glob)
76 76 [255]
77 77
78 78 $ killdaemons.py $DAEMON_PIDS
79 79
80 80 $ cat error.log
81 81 readline(1 from 65537) -> (1) G
82 82 read limit reached; closing socket
83 83
84 84 $ rm -f error.log
85 85
86 86 Same failure, but server reads full HTTP request line
87 87
88 88 $ hg serve --config badserver.closeafterrecvbytes=40 -p $HGPORT -d --pid-file=hg.pid -E error.log
89 89 $ cat hg.pid > $DAEMON_PIDS
90 90 $ hg clone http://localhost:$HGPORT/ clone
91 abort: error: bad HTTP status line: ''
91 abort: error: bad HTTP status line: * (glob)
92 92 [255]
93 93
94 94 $ killdaemons.py $DAEMON_PIDS
95 95
96 96 $ cat error.log
97 97 readline(40 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
98 98 readline(7 from -1) -> (7) Accept-
99 99 read limit reached; closing socket
100 100
101 101 $ rm -f error.log
102 102
103 103 Failure on subsequent HTTP request on the same socket (cmd?batch)
104 104
105 105 $ hg serve --config badserver.closeafterrecvbytes=210,223 -p $HGPORT -d --pid-file=hg.pid -E error.log
106 106 $ cat hg.pid > $DAEMON_PIDS
107 107 $ hg clone http://localhost:$HGPORT/ clone
108 abort: error: bad HTTP status line: ''
108 abort: error: bad HTTP status line: * (glob)
109 109 [255]
110 110
111 111 $ killdaemons.py $DAEMON_PIDS
112 112
113 113 $ cat error.log
114 114 readline(210 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
115 115 readline(177 from -1) -> (27) Accept-Encoding: identity\r\n
116 116 readline(150 from -1) -> (35) accept: application/mercurial-0.1\r\n
117 117 readline(115 from -1) -> (*) host: localhost:$HGPORT\r\n (glob)
118 118 readline(* from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
119 119 readline(* from -1) -> (2) \r\n (glob)
120 120 write(36) -> HTTP/1.1 200 Script output follows\r\n
121 121 write(23) -> Server: badhttpserver\r\n
122 122 write(37) -> Date: $HTTP_DATE$\r\n
123 123 write(41) -> Content-Type: application/mercurial-0.1\r\n
124 124 write(21) -> Content-Length: 450\r\n
125 125 write(2) -> \r\n
126 126 write(450) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
127 127 readline(4? from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob)
128 128 readline(1? from -1) -> (1?) Accept-Encoding* (glob)
129 129 read limit reached; closing socket
130 130 readline(223 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
131 131 readline(197 from -1) -> (27) Accept-Encoding: identity\r\n
132 132 readline(170 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
133 133 readline(141 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
134 134 readline(100 from -1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
135 135 readline(39 from -1) -> (35) accept: application/mercurial-0.1\r\n
136 136 readline(4 from -1) -> (4) host
137 137 read limit reached; closing socket
138 138
139 139 $ rm -f error.log
140 140
141 141 Failure to read getbundle HTTP request
142 142
143 143 $ hg serve --config badserver.closeafterrecvbytes=308,317,304 -p $HGPORT -d --pid-file=hg.pid -E error.log
144 144 $ cat hg.pid > $DAEMON_PIDS
145 145 $ hg clone http://localhost:$HGPORT/ clone
146 146 requesting all changes
147 abort: error: bad HTTP status line: ''
147 abort: error: bad HTTP status line: * (glob)
148 148 [255]
149 149
150 150 $ killdaemons.py $DAEMON_PIDS
151 151
152 152 $ cat error.log
153 153 readline(1 from -1) -> (1) x (?)
154 154 readline(1 from -1) -> (1) x (?)
155 155 readline(308 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
156 156 readline(275 from -1) -> (27) Accept-Encoding: identity\r\n
157 157 readline(248 from -1) -> (35) accept: application/mercurial-0.1\r\n
158 158 readline(213 from -1) -> (*) host: localhost:$HGPORT\r\n (glob)
159 159 readline(* from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
160 160 readline(* from -1) -> (2) \r\n (glob)
161 161 write(36) -> HTTP/1.1 200 Script output follows\r\n
162 162 write(23) -> Server: badhttpserver\r\n
163 163 write(37) -> Date: $HTTP_DATE$\r\n
164 164 write(41) -> Content-Type: application/mercurial-0.1\r\n
165 165 write(21) -> Content-Length: 450\r\n
166 166 write(2) -> \r\n
167 167 write(450) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
168 168 readline(13? from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob)
169 169 readline(1?? from -1) -> (27) Accept-Encoding: identity\r\n (glob)
170 170 readline(8? from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
171 171 readline(5? from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n (glob)
172 172 readline(1? from -1) -> (1?) x-hgproto-1:* (glob)
173 173 read limit reached; closing socket
174 174 readline(317 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
175 175 readline(291 from -1) -> (27) Accept-Encoding: identity\r\n
176 176 readline(264 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
177 177 readline(235 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
178 178 readline(194 from -1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
179 179 readline(133 from -1) -> (35) accept: application/mercurial-0.1\r\n
180 180 readline(98 from -1) -> (*) host: localhost:$HGPORT\r\n (glob)
181 181 readline(* from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
182 182 readline(* from -1) -> (2) \r\n (glob)
183 183 write(36) -> HTTP/1.1 200 Script output follows\r\n
184 184 write(23) -> Server: badhttpserver\r\n
185 185 write(37) -> Date: $HTTP_DATE$\r\n
186 186 write(41) -> Content-Type: application/mercurial-0.1\r\n
187 187 write(20) -> Content-Length: 42\r\n
188 188 write(2) -> \r\n
189 189 write(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
190 190 readline(* from 65537) -> (*) GET /?cmd=getbundle HTTP* (glob)
191 191 read limit reached; closing socket
192 192 readline(304 from 65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
193 193 readline(274 from -1) -> (27) Accept-Encoding: identity\r\n
194 194 readline(247 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
195 195 readline(218 from -1) -> (218) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtag
196 196 read limit reached; closing socket
197 197
198 198 $ rm -f error.log
199 199
200 200 Now do a variation using POST to send arguments
201 201
202 202 $ hg serve --config experimental.httppostargs=true --config badserver.closeafterrecvbytes=329,344 -p $HGPORT -d --pid-file=hg.pid -E error.log
203 203 $ cat hg.pid > $DAEMON_PIDS
204 204
205 205 $ hg clone http://localhost:$HGPORT/ clone
206 abort: error: bad HTTP status line: ''
206 abort: error: bad HTTP status line: * (glob)
207 207 [255]
208 208
209 209 $ killdaemons.py $DAEMON_PIDS
210 210
211 211 $ cat error.log
212 212 readline(329 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
213 213 readline(296 from -1) -> (27) Accept-Encoding: identity\r\n
214 214 readline(269 from -1) -> (35) accept: application/mercurial-0.1\r\n
215 215 readline(234 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
216 216 readline(* from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
217 217 readline(* from -1) -> (2) \r\n (glob)
218 218 write(36) -> HTTP/1.1 200 Script output follows\r\n
219 219 write(23) -> Server: badhttpserver\r\n
220 220 write(37) -> Date: $HTTP_DATE$\r\n
221 221 write(41) -> Content-Type: application/mercurial-0.1\r\n
222 222 write(21) -> Content-Length: 463\r\n
223 223 write(2) -> \r\n
224 224 write(463) -> batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx httppostargs known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
225 225 readline(1?? from 65537) -> (27) POST /?cmd=batch HTTP/1.1\r\n (glob)
226 226 readline(1?? from -1) -> (27) Accept-Encoding: identity\r\n (glob)
227 227 readline(1?? from -1) -> (41) content-type: application/mercurial-0.1\r\n (glob)
228 228 readline(6? from -1) -> (33) vary: X-HgArgs-Post,X-HgProto-1\r\n (glob)
229 229 readline(3? from -1) -> (19) x-hgargs-post: 28\r\n (glob)
230 230 readline(1? from -1) -> (1?) x-hgproto-1: * (glob)
231 231 read limit reached; closing socket
232 232 readline(344 from 65537) -> (27) POST /?cmd=batch HTTP/1.1\r\n
233 233 readline(317 from -1) -> (27) Accept-Encoding: identity\r\n
234 234 readline(290 from -1) -> (41) content-type: application/mercurial-0.1\r\n
235 235 readline(249 from -1) -> (33) vary: X-HgArgs-Post,X-HgProto-1\r\n
236 236 readline(216 from -1) -> (19) x-hgargs-post: 28\r\n
237 237 readline(197 from -1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
238 238 readline(136 from -1) -> (35) accept: application/mercurial-0.1\r\n
239 239 readline(101 from -1) -> (20) content-length: 28\r\n
240 240 readline(81 from -1) -> (*) host: localhost:$HGPORT\r\n (glob)
241 241 readline(* from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
242 242 readline(* from -1) -> (2) \r\n (glob)
243 243 read(* from 28) -> (*) cmds=* (glob)
244 244 read limit reached, closing socket
245 245 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
246 246
247 247 $ rm -f error.log
248 248
249 249 Now move on to partial server responses
250 250
251 251 Server sends a single character from the HTTP response line
252 252
253 253 $ hg serve --config badserver.closeaftersendbytes=1 -p $HGPORT -d --pid-file=hg.pid -E error.log
254 254 $ cat hg.pid > $DAEMON_PIDS
255 255
256 256 $ hg clone http://localhost:$HGPORT/ clone
257 257 abort: error: bad HTTP status line: H
258 258 [255]
259 259
260 260 $ killdaemons.py $DAEMON_PIDS
261 261
262 262 $ cat error.log
263 263 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
264 264 readline(-1) -> (27) Accept-Encoding: identity\r\n
265 265 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
266 266 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
267 267 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
268 268 readline(-1) -> (2) \r\n
269 269 write(1 from 36) -> (0) H
270 270 write limit reached; closing socket
271 271 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
272 272
273 273 $ rm -f error.log
274 274
275 275 Server sends an incomplete capabilities response body
276 276
277 277 $ hg serve --config badserver.closeaftersendbytes=180 -p $HGPORT -d --pid-file=hg.pid -E error.log
278 278 $ cat hg.pid > $DAEMON_PIDS
279 279
280 280 $ hg clone http://localhost:$HGPORT/ clone
281 281 abort: HTTP request error (incomplete response; expected 450 bytes got 20)
282 282 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
283 283 [255]
284 284
285 285 $ killdaemons.py $DAEMON_PIDS
286 286
287 287 $ cat error.log
288 288 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
289 289 readline(-1) -> (27) Accept-Encoding: identity\r\n
290 290 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
291 291 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
292 292 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
293 293 readline(-1) -> (2) \r\n
294 294 write(36 from 36) -> (144) HTTP/1.1 200 Script output follows\r\n
295 295 write(23 from 23) -> (121) Server: badhttpserver\r\n
296 296 write(37 from 37) -> (84) Date: $HTTP_DATE$\r\n
297 297 write(41 from 41) -> (43) Content-Type: application/mercurial-0.1\r\n
298 298 write(21 from 21) -> (22) Content-Length: 450\r\n
299 299 write(2 from 2) -> (20) \r\n
300 300 write(20 from 450) -> (0) batch branchmap bund
301 301 write limit reached; closing socket
302 302
303 303 $ rm -f error.log
304 304
305 305 Server sends incomplete headers for batch request
306 306
307 307 $ hg serve --config badserver.closeaftersendbytes=728 -p $HGPORT -d --pid-file=hg.pid -E error.log
308 308 $ cat hg.pid > $DAEMON_PIDS
309 309
310 310 TODO this output is horrible
311 311
312 312 $ hg clone http://localhost:$HGPORT/ clone
313 313 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
314 314 ---%<--- (applicat)
315 315
316 316 ---%<---
317 317 !
318 318 [255]
319 319
320 320 $ killdaemons.py $DAEMON_PIDS
321 321
322 322 $ cat error.log
323 323 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
324 324 readline(-1) -> (27) Accept-Encoding: identity\r\n
325 325 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
326 326 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
327 327 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
328 328 readline(-1) -> (2) \r\n
329 329 write(36 from 36) -> (692) HTTP/1.1 200 Script output follows\r\n
330 330 write(23 from 23) -> (669) Server: badhttpserver\r\n
331 331 write(37 from 37) -> (632) Date: $HTTP_DATE$\r\n
332 332 write(41 from 41) -> (591) Content-Type: application/mercurial-0.1\r\n
333 333 write(21 from 21) -> (570) Content-Length: 450\r\n
334 334 write(2 from 2) -> (568) \r\n
335 335 write(450 from 450) -> (118) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
336 336 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
337 337 readline(-1) -> (27) Accept-Encoding: identity\r\n
338 338 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
339 339 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
340 340 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
341 341 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
342 342 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
343 343 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
344 344 readline(-1) -> (2) \r\n
345 345 write(36 from 36) -> (82) HTTP/1.1 200 Script output follows\r\n
346 346 write(23 from 23) -> (59) Server: badhttpserver\r\n
347 347 write(37 from 37) -> (22) Date: $HTTP_DATE$\r\n
348 348 write(22 from 41) -> (0) Content-Type: applicat
349 349 write limit reached; closing socket
350 350 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
351 351
352 352 $ rm -f error.log
353 353
354 354 Server sends an incomplete HTTP response body to batch request
355 355
356 356 $ hg serve --config badserver.closeaftersendbytes=793 -p $HGPORT -d --pid-file=hg.pid -E error.log
357 357 $ cat hg.pid > $DAEMON_PIDS
358 358
359 359 TODO client spews a stack due to uncaught ValueError in batch.results()
360 360 #if no-chg
361 361 $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
362 362 [1]
363 363 #else
364 364 $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
365 365 [255]
366 366 #endif
367 367
368 368 $ killdaemons.py $DAEMON_PIDS
369 369
370 370 $ cat error.log
371 371 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
372 372 readline(-1) -> (27) Accept-Encoding: identity\r\n
373 373 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
374 374 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
375 375 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
376 376 readline(-1) -> (2) \r\n
377 377 write(36 from 36) -> (757) HTTP/1.1 200 Script output follows\r\n
378 378 write(23 from 23) -> (734) Server: badhttpserver\r\n
379 379 write(37 from 37) -> (697) Date: $HTTP_DATE$\r\n
380 380 write(41 from 41) -> (656) Content-Type: application/mercurial-0.1\r\n
381 381 write(21 from 21) -> (635) Content-Length: 450\r\n
382 382 write(2 from 2) -> (633) \r\n
383 383 write(450 from 450) -> (183) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
384 384 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
385 385 readline(-1) -> (27) Accept-Encoding: identity\r\n
386 386 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
387 387 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
388 388 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
389 389 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
390 390 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
391 391 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
392 392 readline(-1) -> (2) \r\n
393 393 write(36 from 36) -> (147) HTTP/1.1 200 Script output follows\r\n
394 394 write(23 from 23) -> (124) Server: badhttpserver\r\n
395 395 write(37 from 37) -> (87) Date: $HTTP_DATE$\r\n
396 396 write(41 from 41) -> (46) Content-Type: application/mercurial-0.1\r\n
397 397 write(20 from 20) -> (26) Content-Length: 42\r\n
398 398 write(2 from 2) -> (24) \r\n
399 399 write(24 from 42) -> (0) 96ee1d7354c4ad7372047672
400 400 write limit reached; closing socket
401 401
402 402 $ rm -f error.log
403 403
404 404 Server sends incomplete headers for getbundle response
405 405
406 406 $ hg serve --config badserver.closeaftersendbytes=940 -p $HGPORT -d --pid-file=hg.pid -E error.log
407 407 $ cat hg.pid > $DAEMON_PIDS
408 408
409 409 TODO this output is terrible
410 410
411 411 $ hg clone http://localhost:$HGPORT/ clone
412 412 requesting all changes
413 413 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
414 414 ---%<--- (application/mercuri)
415 415
416 416 ---%<---
417 417 !
418 418 [255]
419 419
420 420 $ killdaemons.py $DAEMON_PIDS
421 421
422 422 $ cat error.log
423 423 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
424 424 readline(-1) -> (27) Accept-Encoding: identity\r\n
425 425 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
426 426 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
427 427 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
428 428 readline(-1) -> (2) \r\n
429 429 write(36 from 36) -> (904) HTTP/1.1 200 Script output follows\r\n
430 430 write(23 from 23) -> (881) Server: badhttpserver\r\n
431 431 write(37 from 37) -> (844) Date: $HTTP_DATE$\r\n
432 432 write(41 from 41) -> (803) Content-Type: application/mercurial-0.1\r\n
433 433 write(21 from 21) -> (782) Content-Length: 450\r\n
434 434 write(2 from 2) -> (780) \r\n
435 435 write(450 from 450) -> (330) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
436 436 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
437 437 readline(-1) -> (27) Accept-Encoding: identity\r\n
438 438 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
439 439 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
440 440 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
441 441 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
442 442 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
443 443 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
444 444 readline(-1) -> (2) \r\n
445 445 write(36 from 36) -> (294) HTTP/1.1 200 Script output follows\r\n
446 446 write(23 from 23) -> (271) Server: badhttpserver\r\n
447 447 write(37 from 37) -> (234) Date: $HTTP_DATE$\r\n
448 448 write(41 from 41) -> (193) Content-Type: application/mercurial-0.1\r\n
449 449 write(20 from 20) -> (173) Content-Length: 42\r\n
450 450 write(2 from 2) -> (171) \r\n
451 451 write(42 from 42) -> (129) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
452 452 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
453 453 readline(-1) -> (27) Accept-Encoding: identity\r\n
454 454 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
455 455 readline(-1) -> (461) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
456 456 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
457 457 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
458 458 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
459 459 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
460 460 readline(-1) -> (2) \r\n
461 461 write(36 from 36) -> (93) HTTP/1.1 200 Script output follows\r\n
462 462 write(23 from 23) -> (70) Server: badhttpserver\r\n
463 463 write(37 from 37) -> (33) Date: $HTTP_DATE$\r\n
464 464 write(33 from 41) -> (0) Content-Type: application/mercuri
465 465 write limit reached; closing socket
466 466 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
467 467
468 468 $ rm -f error.log
469 469
470 470 Server stops before it sends transfer encoding
471 471
472 472 $ hg serve --config badserver.closeaftersendbytes=973 -p $HGPORT -d --pid-file=hg.pid -E error.log
473 473 $ cat hg.pid > $DAEMON_PIDS
474 474
475 475 $ hg clone http://localhost:$HGPORT/ clone
476 476 requesting all changes
477 477 abort: stream ended unexpectedly (got 0 bytes, expected 1)
478 478 [255]
479 479
480 480 $ killdaemons.py $DAEMON_PIDS
481 481
482 482 $ tail -4 error.log
483 483 write(41 from 41) -> (25) Content-Type: application/mercurial-0.2\r\n
484 484 write(25 from 28) -> (0) Transfer-Encoding: chunke
485 485 write limit reached; closing socket
486 486 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
487 487
488 488 $ rm -f error.log
489 489
490 490 Server sends empty HTTP body for getbundle
491 491
492 492 $ hg serve --config badserver.closeaftersendbytes=978 -p $HGPORT -d --pid-file=hg.pid -E error.log
493 493 $ cat hg.pid > $DAEMON_PIDS
494 494
495 495 $ hg clone http://localhost:$HGPORT/ clone
496 496 requesting all changes
497 497 abort: HTTP request error (incomplete response)
498 498 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
499 499 [255]
500 500
501 501 $ killdaemons.py $DAEMON_PIDS
502 502
503 503 $ cat error.log
504 504 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
505 505 readline(-1) -> (27) Accept-Encoding: identity\r\n
506 506 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
507 507 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
508 508 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
509 509 readline(-1) -> (2) \r\n
510 510 write(36 from 36) -> (942) HTTP/1.1 200 Script output follows\r\n
511 511 write(23 from 23) -> (919) Server: badhttpserver\r\n
512 512 write(37 from 37) -> (882) Date: $HTTP_DATE$\r\n
513 513 write(41 from 41) -> (841) Content-Type: application/mercurial-0.1\r\n
514 514 write(21 from 21) -> (820) Content-Length: 450\r\n
515 515 write(2 from 2) -> (818) \r\n
516 516 write(450 from 450) -> (368) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
517 517 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
518 518 readline(-1) -> (27) Accept-Encoding: identity\r\n
519 519 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
520 520 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
521 521 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
522 522 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
523 523 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
524 524 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
525 525 readline(-1) -> (2) \r\n
526 526 write(36 from 36) -> (332) HTTP/1.1 200 Script output follows\r\n
527 527 write(23 from 23) -> (309) Server: badhttpserver\r\n
528 528 write(37 from 37) -> (272) Date: $HTTP_DATE$\r\n
529 529 write(41 from 41) -> (231) Content-Type: application/mercurial-0.1\r\n
530 530 write(20 from 20) -> (211) Content-Length: 42\r\n
531 531 write(2 from 2) -> (209) \r\n
532 532 write(42 from 42) -> (167) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
533 533 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
534 534 readline(-1) -> (27) Accept-Encoding: identity\r\n
535 535 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
536 536 readline(-1) -> (461) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
537 537 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
538 538 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
539 539 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
540 540 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
541 541 readline(-1) -> (2) \r\n
542 542 write(36 from 36) -> (131) HTTP/1.1 200 Script output follows\r\n
543 543 write(23 from 23) -> (108) Server: badhttpserver\r\n
544 544 write(37 from 37) -> (71) Date: $HTTP_DATE$\r\n
545 545 write(41 from 41) -> (30) Content-Type: application/mercurial-0.2\r\n
546 546 write(28 from 28) -> (2) Transfer-Encoding: chunked\r\n
547 547 write(2 from 2) -> (0) \r\n
548 548 write limit reached; closing socket
549 549 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
550 550
551 551 $ rm -f error.log
552 552
553 553 Server sends partial compression string
554 554
555 555 $ hg serve --config badserver.closeaftersendbytes=1002 -p $HGPORT -d --pid-file=hg.pid -E error.log
556 556 $ cat hg.pid > $DAEMON_PIDS
557 557
558 558 $ hg clone http://localhost:$HGPORT/ clone
559 559 requesting all changes
560 560 abort: HTTP request error (incomplete response)
561 561 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
562 562 [255]
563 563
564 564 $ killdaemons.py $DAEMON_PIDS
565 565
566 566 $ cat error.log
567 567 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
568 568 readline(-1) -> (27) Accept-Encoding: identity\r\n
569 569 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
570 570 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
571 571 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
572 572 readline(-1) -> (2) \r\n
573 573 write(36 from 36) -> (966) HTTP/1.1 200 Script output follows\r\n
574 574 write(23 from 23) -> (943) Server: badhttpserver\r\n
575 575 write(37 from 37) -> (906) Date: $HTTP_DATE$\r\n
576 576 write(41 from 41) -> (865) Content-Type: application/mercurial-0.1\r\n
577 577 write(21 from 21) -> (844) Content-Length: 450\r\n
578 578 write(2 from 2) -> (842) \r\n
579 579 write(450 from 450) -> (392) batch branchmap $USUAL_BUNDLE2_CAPS_NO_PHASES$ changegroupsubset compression=none getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
580 580 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
581 581 readline(-1) -> (27) Accept-Encoding: identity\r\n
582 582 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
583 583 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
584 584 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
585 585 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
586 586 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
587 587 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
588 588 readline(-1) -> (2) \r\n
589 589 write(36 from 36) -> (356) HTTP/1.1 200 Script output follows\r\n
590 590 write(23 from 23) -> (333) Server: badhttpserver\r\n
591 591 write(37 from 37) -> (296) Date: $HTTP_DATE$\r\n
592 592 write(41 from 41) -> (255) Content-Type: application/mercurial-0.1\r\n
593 593 write(20 from 20) -> (235) Content-Length: 42\r\n
594 594 write(2 from 2) -> (233) \r\n
595 595 write(42 from 42) -> (191) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
596 596 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
597 597 readline(-1) -> (27) Accept-Encoding: identity\r\n
598 598 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
599 599 readline(-1) -> (461) x-hgarg-1: bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
600 600 readline(-1) -> (61) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
601 601 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
602 602 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
603 603 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
604 604 readline(-1) -> (2) \r\n
605 605 write(36 from 36) -> (155) HTTP/1.1 200 Script output follows\r\n
606 606 write(23 from 23) -> (132) Server: badhttpserver\r\n
607 607 write(37 from 37) -> (95) Date: $HTTP_DATE$\r\n
608 608 write(41 from 41) -> (54) Content-Type: application/mercurial-0.2\r\n
609 609 write(28 from 28) -> (26) Transfer-Encoding: chunked\r\n
610 610 write(2 from 2) -> (24) \r\n
611 611 write(6 from 6) -> (18) 1\\r\\n\x04\\r\\n (esc)
612 612 write(9 from 9) -> (9) 4\r\nnone\r\n
613 613 write(9 from 9) -> (0) 4\r\nHG20\r\n
614 614 write limit reached; closing socket
615 615 write(27) -> 15\r\nInternal Server Error\r\n
616 616
617 617 $ rm -f error.log
618 618
619 619 Server sends partial bundle2 header magic
620 620
621 621 $ hg serve --config badserver.closeaftersendbytes=999 -p $HGPORT -d --pid-file=hg.pid -E error.log
622 622 $ cat hg.pid > $DAEMON_PIDS
623 623
624 624 $ hg clone http://localhost:$HGPORT/ clone
625 625 requesting all changes
626 626 abort: HTTP request error (incomplete response; expected 4 bytes got 3)
627 627 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
628 628 [255]
629 629
630 630 $ killdaemons.py $DAEMON_PIDS
631 631
632 632 $ tail -7 error.log
633 633 write(28 from 28) -> (23) Transfer-Encoding: chunked\r\n
634 634 write(2 from 2) -> (21) \r\n
635 635 write(6 from 6) -> (15) 1\\r\\n\x04\\r\\n (esc)
636 636 write(9 from 9) -> (6) 4\r\nnone\r\n
637 637 write(6 from 9) -> (0) 4\r\nHG2
638 638 write limit reached; closing socket
639 639 write(27) -> 15\r\nInternal Server Error\r\n
640 640
641 641 $ rm -f error.log
642 642
643 643 Server sends incomplete bundle2 stream params length
644 644
645 645 $ hg serve --config badserver.closeaftersendbytes=1008 -p $HGPORT -d --pid-file=hg.pid -E error.log
646 646 $ cat hg.pid > $DAEMON_PIDS
647 647
648 648 $ hg clone http://localhost:$HGPORT/ clone
649 649 requesting all changes
650 650 abort: HTTP request error (incomplete response; expected 4 bytes got 3)
651 651 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
652 652 [255]
653 653
654 654 $ killdaemons.py $DAEMON_PIDS
655 655
656 656 $ tail -8 error.log
657 657 write(28 from 28) -> (32) Transfer-Encoding: chunked\r\n
658 658 write(2 from 2) -> (30) \r\n
659 659 write(6 from 6) -> (24) 1\\r\\n\x04\\r\\n (esc)
660 660 write(9 from 9) -> (15) 4\r\nnone\r\n
661 661 write(9 from 9) -> (6) 4\r\nHG20\r\n
662 662 write(6 from 9) -> (0) 4\\r\\n\x00\x00\x00 (esc)
663 663 write limit reached; closing socket
664 664 write(27) -> 15\r\nInternal Server Error\r\n
665 665
666 666 $ rm -f error.log
667 667
668 668 Servers stops after bundle2 stream params header
669 669
670 670 $ hg serve --config badserver.closeaftersendbytes=1011 -p $HGPORT -d --pid-file=hg.pid -E error.log
671 671 $ cat hg.pid > $DAEMON_PIDS
672 672
673 673 $ hg clone http://localhost:$HGPORT/ clone
674 674 requesting all changes
675 675 abort: HTTP request error (incomplete response)
676 676 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
677 677 [255]
678 678
679 679 $ killdaemons.py $DAEMON_PIDS
680 680
681 681 $ tail -8 error.log
682 682 write(28 from 28) -> (35) Transfer-Encoding: chunked\r\n
683 683 write(2 from 2) -> (33) \r\n
684 684 write(6 from 6) -> (27) 1\\r\\n\x04\\r\\n (esc)
685 685 write(9 from 9) -> (18) 4\r\nnone\r\n
686 686 write(9 from 9) -> (9) 4\r\nHG20\r\n
687 687 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
688 688 write limit reached; closing socket
689 689 write(27) -> 15\r\nInternal Server Error\r\n
690 690
691 691 $ rm -f error.log
692 692
693 693 Server stops sending after bundle2 part header length
694 694
695 695 $ hg serve --config badserver.closeaftersendbytes=1020 -p $HGPORT -d --pid-file=hg.pid -E error.log
696 696 $ cat hg.pid > $DAEMON_PIDS
697 697
698 698 $ hg clone http://localhost:$HGPORT/ clone
699 699 requesting all changes
700 700 abort: HTTP request error (incomplete response)
701 701 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
702 702 [255]
703 703
704 704 $ killdaemons.py $DAEMON_PIDS
705 705
706 706 $ tail -9 error.log
707 707 write(28 from 28) -> (44) Transfer-Encoding: chunked\r\n
708 708 write(2 from 2) -> (42) \r\n
709 709 write(6 from 6) -> (36) 1\\r\\n\x04\\r\\n (esc)
710 710 write(9 from 9) -> (27) 4\r\nnone\r\n
711 711 write(9 from 9) -> (18) 4\r\nHG20\r\n
712 712 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
713 713 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
714 714 write limit reached; closing socket
715 715 write(27) -> 15\r\nInternal Server Error\r\n
716 716
717 717 $ rm -f error.log
718 718
719 719 Server stops sending after bundle2 part header
720 720
721 721 $ hg serve --config badserver.closeaftersendbytes=1067 -p $HGPORT -d --pid-file=hg.pid -E error.log
722 722 $ cat hg.pid > $DAEMON_PIDS
723 723
724 724 $ hg clone http://localhost:$HGPORT/ clone
725 725 requesting all changes
726 726 adding changesets
727 727 transaction abort!
728 728 rollback completed
729 729 abort: HTTP request error (incomplete response)
730 730 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
731 731 [255]
732 732
733 733 $ killdaemons.py $DAEMON_PIDS
734 734
735 735 $ tail -10 error.log
736 736 write(28 from 28) -> (91) Transfer-Encoding: chunked\r\n
737 737 write(2 from 2) -> (89) \r\n
738 738 write(6 from 6) -> (83) 1\\r\\n\x04\\r\\n (esc)
739 739 write(9 from 9) -> (74) 4\r\nnone\r\n
740 740 write(9 from 9) -> (65) 4\r\nHG20\r\n
741 741 write(9 from 9) -> (56) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
742 742 write(9 from 9) -> (47) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
743 743 write(47 from 47) -> (0) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
744 744 write limit reached; closing socket
745 745 write(27) -> 15\r\nInternal Server Error\r\n
746 746
747 747 $ rm -f error.log
748 748
749 749 Server stops after bundle2 part payload chunk size
750 750
751 751 $ hg serve --config badserver.closeaftersendbytes=1088 -p $HGPORT -d --pid-file=hg.pid -E error.log
752 752 $ cat hg.pid > $DAEMON_PIDS
753 753
754 754 $ hg clone http://localhost:$HGPORT/ clone
755 755 requesting all changes
756 756 adding changesets
757 757 transaction abort!
758 758 rollback completed
759 759 abort: HTTP request error (incomplete response; expected 466 bytes got 7)
760 760 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
761 761 [255]
762 762
763 763 $ killdaemons.py $DAEMON_PIDS
764 764
765 765 $ tail -11 error.log
766 766 write(2 from 2) -> (110) \r\n
767 767 write(6 from 6) -> (104) 1\\r\\n\x04\\r\\n (esc)
768 768 write(9 from 9) -> (95) 4\r\nnone\r\n
769 769 write(9 from 9) -> (86) 4\r\nHG20\r\n
770 770 write(9 from 9) -> (77) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
771 771 write(9 from 9) -> (68) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
772 772 write(47 from 47) -> (21) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
773 773 write(9 from 9) -> (12) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
774 774 write(12 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1d (esc)
775 775 write limit reached; closing socket
776 776 write(27) -> 15\r\nInternal Server Error\r\n
777 777
778 778 $ rm -f error.log
779 779
780 780 Server stops sending in middle of bundle2 payload chunk
781 781
782 782 $ hg serve --config badserver.closeaftersendbytes=1549 -p $HGPORT -d --pid-file=hg.pid -E error.log
783 783 $ cat hg.pid > $DAEMON_PIDS
784 784
785 785 $ hg clone http://localhost:$HGPORT/ clone
786 786 requesting all changes
787 787 adding changesets
788 788 transaction abort!
789 789 rollback completed
790 790 abort: HTTP request error (incomplete response)
791 791 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
792 792 [255]
793 793
794 794 $ killdaemons.py $DAEMON_PIDS
795 795
796 796 $ tail -12 error.log
797 797 write(28 from 28) -> (573) Transfer-Encoding: chunked\r\n
798 798 write(2 from 2) -> (571) \r\n
799 799 write(6 from 6) -> (565) 1\\r\\n\x04\\r\\n (esc)
800 800 write(9 from 9) -> (556) 4\r\nnone\r\n
801 801 write(9 from 9) -> (547) 4\r\nHG20\r\n
802 802 write(9 from 9) -> (538) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
803 803 write(9 from 9) -> (529) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
804 804 write(47 from 47) -> (482) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
805 805 write(9 from 9) -> (473) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
806 806 write(473 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
807 807 write limit reached; closing socket
808 808 write(27) -> 15\r\nInternal Server Error\r\n
809 809
810 810 $ rm -f error.log
811 811
812 812 Server stops sending after 0 length payload chunk size
813 813
814 814 $ hg serve --config badserver.closeaftersendbytes=1580 -p $HGPORT -d --pid-file=hg.pid -E error.log
815 815 $ cat hg.pid > $DAEMON_PIDS
816 816
817 817 $ hg clone http://localhost:$HGPORT/ clone
818 818 requesting all changes
819 819 adding changesets
820 820 adding manifests
821 821 adding file changes
822 822 added 1 changesets with 1 changes to 1 files
823 823 transaction abort!
824 824 rollback completed
825 825 abort: HTTP request error (incomplete response; expected 32 bytes got 9)
826 826 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
827 827 [255]
828 828
829 829 $ killdaemons.py $DAEMON_PIDS
830 830
831 831 $ tail -13 error.log
832 832 write(6 from 6) -> (596) 1\\r\\n\x04\\r\\n (esc)
833 833 write(9 from 9) -> (587) 4\r\nnone\r\n
834 834 write(9 from 9) -> (578) 4\r\nHG20\r\n
835 835 write(9 from 9) -> (569) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
836 836 write(9 from 9) -> (560) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
837 837 write(47 from 47) -> (513) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
838 838 write(9 from 9) -> (504) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
839 839 write(473 from 473) -> (31) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
840 840 write(9 from 9) -> (22) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
841 841 write(9 from 9) -> (13) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
842 842 write(13 from 38) -> (0) 20\\r\\n\x08LISTKEYS (esc)
843 843 write limit reached; closing socket
844 844 write(27) -> 15\r\nInternal Server Error\r\n
845 845
846 846 $ rm -f error.log
847 847
848 848 Server stops sending after 0 part bundle part header (indicating end of bundle2 payload)
849 849 This is before the 0 size chunked transfer part that signals end of HTTP response.
850 850
851 851 # $ hg serve --config badserver.closeaftersendbytes=1755 -p $HGPORT -d --pid-file=hg.pid -E error.log
852 852 $ hg serve --config badserver.closeaftersendbytes=1862 -p $HGPORT -d --pid-file=hg.pid -E error.log
853 853 $ cat hg.pid > $DAEMON_PIDS
854 854
855 855 $ hg clone http://localhost:$HGPORT/ clone
856 856 requesting all changes
857 857 adding changesets
858 858 adding manifests
859 859 adding file changes
860 860 added 1 changesets with 1 changes to 1 files
861 861 new changesets 96ee1d7354c4
862 862 updating to branch default
863 863 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
864 864
865 865 $ killdaemons.py $DAEMON_PIDS
866 866
867 867 $ tail -22 error.log
868 868 write(9 from 9) -> (851) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
869 869 write(9 from 9) -> (842) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
870 870 write(47 from 47) -> (795) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
871 871 write(9 from 9) -> (786) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
872 872 write(473 from 473) -> (313) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
873 873 write(9 from 9) -> (304) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
874 874 write(9 from 9) -> (295) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
875 875 write(38 from 38) -> (257) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
876 876 write(9 from 9) -> (248) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
877 877 write(64 from 64) -> (184) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
878 878 write(9 from 9) -> (175) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
879 879 write(9 from 9) -> (166) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
880 880 write(41 from 41) -> (125) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
881 881 write(9 from 9) -> (116) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
882 882 write(9 from 9) -> (107) 4\\r\\n\x00\x00\x00\x1d\\r\\n (esc)
883 883 write(35 from 35) -> (72) 1d\\r\\n\x16cache:rev-branch-cache\x00\x00\x00\x03\x00\x00\\r\\n (esc)
884 884 write(9 from 9) -> (63) 4\\r\\n\x00\x00\x00'\\r\\n (esc)
885 885 write(45 from 45) -> (18) 27\\r\\n\x00\x00\x00\x07\x00\x00\x00\x01\x00\x00\x00\x00default\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\\r\\n (esc)
886 886 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
887 887 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
888 888 write limit reached; closing socket
889 889 write(27) -> 15\r\nInternal Server Error\r\n
890 890
891 891 $ rm -f error.log
892 892 $ rm -rf clone
893 893
894 894 Server sends a size 0 chunked-transfer size without terminating \r\n
895 895
896 896 $ hg serve --config badserver.closeaftersendbytes=1865 -p $HGPORT -d --pid-file=hg.pid -E error.log
897 897 $ cat hg.pid > $DAEMON_PIDS
898 898
899 899 $ hg clone http://localhost:$HGPORT/ clone
900 900 requesting all changes
901 901 adding changesets
902 902 adding manifests
903 903 adding file changes
904 904 added 1 changesets with 1 changes to 1 files
905 905 new changesets 96ee1d7354c4
906 906 updating to branch default
907 907 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
908 908
909 909 $ killdaemons.py $DAEMON_PIDS
910 910
911 911 $ tail -23 error.log
912 912 write(9 from 9) -> (854) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
913 913 write(9 from 9) -> (845) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
914 914 write(47 from 47) -> (798) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
915 915 write(9 from 9) -> (789) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
916 916 write(473 from 473) -> (316) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
917 917 write(9 from 9) -> (307) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
918 918 write(9 from 9) -> (298) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
919 919 write(38 from 38) -> (260) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
920 920 write(9 from 9) -> (251) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
921 921 write(64 from 64) -> (187) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
922 922 write(9 from 9) -> (178) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
923 923 write(9 from 9) -> (169) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
924 924 write(41 from 41) -> (128) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
925 925 write(9 from 9) -> (119) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
926 926 write(9 from 9) -> (110) 4\\r\\n\x00\x00\x00\x1d\\r\\n (esc)
927 927 write(35 from 35) -> (75) 1d\\r\\n\x16cache:rev-branch-cache\x00\x00\x00\x03\x00\x00\\r\\n (esc)
928 928 write(9 from 9) -> (66) 4\\r\\n\x00\x00\x00'\\r\\n (esc)
929 929 write(45 from 45) -> (21) 27\\r\\n\x00\x00\x00\x07\x00\x00\x00\x01\x00\x00\x00\x00default\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\\r\\n (esc)
930 930 write(9 from 9) -> (12) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
931 931 write(9 from 9) -> (3) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
932 932 write(3 from 5) -> (0) 0\r\n
933 933 write limit reached; closing socket
934 934 write(27) -> 15\r\nInternal Server Error\r\n
935 935
936 936 $ rm -f error.log
937 937 $ rm -rf clone
General Comments 0
You need to be logged in to leave comments. Login now