##// END OF EJS Templates
config: update evolution-related config...
Boris Feld -
r34865:fec79a3f default
parent child Browse files
Show More
@@ -1,1106 +1,1115 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 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 if key in self:
71 71 return self[key]
72 72
73 73 # search for a matching generic item
74 74 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
75 75 for item in generics:
76 76 if item._re.match(key):
77 77 return item
78 78
79 79 # fallback to dict get
80 80 return super(itemregister, self).get(key)
81 81
82 82 coreitems = {}
83 83
84 84 def _register(configtable, *args, **kwargs):
85 85 item = configitem(*args, **kwargs)
86 86 section = configtable.setdefault(item.section, itemregister())
87 87 if item.name in section:
88 88 msg = "duplicated config item registration for '%s.%s'"
89 89 raise error.ProgrammingError(msg % (item.section, item.name))
90 90 section[item.name] = item
91 91
92 92 # special value for case where the default is derived from other values
93 93 dynamicdefault = object()
94 94
95 95 # Registering actual config items
96 96
97 97 def getitemregister(configtable):
98 98 return functools.partial(_register, configtable)
99 99
100 100 coreconfigitem = getitemregister(coreitems)
101 101
102 102 coreconfigitem('alias', '.*',
103 103 default=None,
104 104 generic=True,
105 105 )
106 106 coreconfigitem('annotate', 'nodates',
107 107 default=False,
108 108 )
109 109 coreconfigitem('annotate', 'showfunc',
110 110 default=False,
111 111 )
112 112 coreconfigitem('annotate', 'unified',
113 113 default=None,
114 114 )
115 115 coreconfigitem('annotate', 'git',
116 116 default=False,
117 117 )
118 118 coreconfigitem('annotate', 'ignorews',
119 119 default=False,
120 120 )
121 121 coreconfigitem('annotate', 'ignorewsamount',
122 122 default=False,
123 123 )
124 124 coreconfigitem('annotate', 'ignoreblanklines',
125 125 default=False,
126 126 )
127 127 coreconfigitem('annotate', 'ignorewseol',
128 128 default=False,
129 129 )
130 130 coreconfigitem('annotate', 'nobinary',
131 131 default=False,
132 132 )
133 133 coreconfigitem('annotate', 'noprefix',
134 134 default=False,
135 135 )
136 136 coreconfigitem('auth', 'cookiefile',
137 137 default=None,
138 138 )
139 139 # bookmarks.pushing: internal hack for discovery
140 140 coreconfigitem('bookmarks', 'pushing',
141 141 default=list,
142 142 )
143 143 # bundle.mainreporoot: internal hack for bundlerepo
144 144 coreconfigitem('bundle', 'mainreporoot',
145 145 default='',
146 146 )
147 147 # bundle.reorder: experimental config
148 148 coreconfigitem('bundle', 'reorder',
149 149 default='auto',
150 150 )
151 151 coreconfigitem('censor', 'policy',
152 152 default='abort',
153 153 )
154 154 coreconfigitem('chgserver', 'idletimeout',
155 155 default=3600,
156 156 )
157 157 coreconfigitem('chgserver', 'skiphash',
158 158 default=False,
159 159 )
160 160 coreconfigitem('cmdserver', 'log',
161 161 default=None,
162 162 )
163 163 coreconfigitem('color', '.*',
164 164 default=None,
165 165 generic=True,
166 166 )
167 167 coreconfigitem('color', 'mode',
168 168 default='auto',
169 169 )
170 170 coreconfigitem('color', 'pagermode',
171 171 default=dynamicdefault,
172 172 )
173 173 coreconfigitem('commands', 'status.relative',
174 174 default=False,
175 175 )
176 176 coreconfigitem('commands', 'status.skipstates',
177 177 default=[],
178 178 )
179 179 coreconfigitem('commands', 'status.verbose',
180 180 default=False,
181 181 )
182 182 coreconfigitem('commands', 'update.check',
183 183 default=None,
184 184 # Deprecated, remove after 4.4 release
185 185 alias=[('experimental', 'updatecheck')]
186 186 )
187 187 coreconfigitem('commands', 'update.requiredest',
188 188 default=False,
189 189 )
190 190 coreconfigitem('committemplate', '.*',
191 191 default=None,
192 192 generic=True,
193 193 )
194 194 coreconfigitem('debug', 'dirstate.delaywrite',
195 195 default=0,
196 196 )
197 197 coreconfigitem('defaults', '.*',
198 198 default=None,
199 199 generic=True,
200 200 )
201 201 coreconfigitem('devel', 'all-warnings',
202 202 default=False,
203 203 )
204 204 coreconfigitem('devel', 'bundle2.debug',
205 205 default=False,
206 206 )
207 207 coreconfigitem('devel', 'cache-vfs',
208 208 default=None,
209 209 )
210 210 coreconfigitem('devel', 'check-locks',
211 211 default=False,
212 212 )
213 213 coreconfigitem('devel', 'check-relroot',
214 214 default=False,
215 215 )
216 216 coreconfigitem('devel', 'default-date',
217 217 default=None,
218 218 )
219 219 coreconfigitem('devel', 'deprec-warn',
220 220 default=False,
221 221 )
222 222 coreconfigitem('devel', 'disableloaddefaultcerts',
223 223 default=False,
224 224 )
225 225 coreconfigitem('devel', 'warn-empty-changegroup',
226 226 default=False,
227 227 )
228 228 coreconfigitem('devel', 'legacy.exchange',
229 229 default=list,
230 230 )
231 231 coreconfigitem('devel', 'servercafile',
232 232 default='',
233 233 )
234 234 coreconfigitem('devel', 'serverexactprotocol',
235 235 default='',
236 236 )
237 237 coreconfigitem('devel', 'serverrequirecert',
238 238 default=False,
239 239 )
240 240 coreconfigitem('devel', 'strip-obsmarkers',
241 241 default=True,
242 242 )
243 243 coreconfigitem('devel', 'warn-config',
244 244 default=None,
245 245 )
246 246 coreconfigitem('devel', 'warn-config-default',
247 247 default=None,
248 248 )
249 249 coreconfigitem('devel', 'user.obsmarker',
250 250 default=None,
251 251 )
252 252 coreconfigitem('devel', 'warn-config-unknown',
253 253 default=None,
254 254 )
255 255 coreconfigitem('diff', 'nodates',
256 256 default=False,
257 257 )
258 258 coreconfigitem('diff', 'showfunc',
259 259 default=False,
260 260 )
261 261 coreconfigitem('diff', 'unified',
262 262 default=None,
263 263 )
264 264 coreconfigitem('diff', 'git',
265 265 default=False,
266 266 )
267 267 coreconfigitem('diff', 'ignorews',
268 268 default=False,
269 269 )
270 270 coreconfigitem('diff', 'ignorewsamount',
271 271 default=False,
272 272 )
273 273 coreconfigitem('diff', 'ignoreblanklines',
274 274 default=False,
275 275 )
276 276 coreconfigitem('diff', 'ignorewseol',
277 277 default=False,
278 278 )
279 279 coreconfigitem('diff', 'nobinary',
280 280 default=False,
281 281 )
282 282 coreconfigitem('diff', 'noprefix',
283 283 default=False,
284 284 )
285 285 coreconfigitem('email', 'bcc',
286 286 default=None,
287 287 )
288 288 coreconfigitem('email', 'cc',
289 289 default=None,
290 290 )
291 291 coreconfigitem('email', 'charsets',
292 292 default=list,
293 293 )
294 294 coreconfigitem('email', 'from',
295 295 default=None,
296 296 )
297 297 coreconfigitem('email', 'method',
298 298 default='smtp',
299 299 )
300 300 coreconfigitem('email', 'reply-to',
301 301 default=None,
302 302 )
303 303 coreconfigitem('experimental', 'allowdivergence',
304 304 default=False,
305 305 )
306 306 coreconfigitem('experimental', 'archivemetatemplate',
307 307 default=dynamicdefault,
308 308 )
309 309 coreconfigitem('experimental', 'bundle-phases',
310 310 default=False,
311 311 )
312 312 coreconfigitem('experimental', 'bundle2-advertise',
313 313 default=True,
314 314 )
315 315 coreconfigitem('experimental', 'bundle2-output-capture',
316 316 default=False,
317 317 )
318 318 coreconfigitem('experimental', 'bundle2.pushback',
319 319 default=False,
320 320 )
321 321 coreconfigitem('experimental', 'bundle2lazylocking',
322 322 default=False,
323 323 )
324 324 coreconfigitem('experimental', 'bundlecomplevel',
325 325 default=None,
326 326 )
327 327 coreconfigitem('experimental', 'changegroup3',
328 328 default=False,
329 329 )
330 330 coreconfigitem('experimental', 'clientcompressionengines',
331 331 default=list,
332 332 )
333 333 coreconfigitem('experimental', 'copytrace',
334 334 default='on',
335 335 )
336 336 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
337 337 default=100,
338 338 )
339 339 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
340 340 default=100,
341 341 )
342 342 coreconfigitem('experimental', 'crecordtest',
343 343 default=None,
344 344 )
345 345 coreconfigitem('experimental', 'editortmpinhg',
346 346 default=False,
347 347 )
348 348 coreconfigitem('experimental', 'evolution',
349 349 default=list,
350 350 alias=[('experimental', 'stabilization')],
351 351 )
352 coreconfigitem('experimental', 'evolution.allowunstable',
353 default=None,
354 )
355 coreconfigitem('experimental', 'evolution.createmarkers',
356 default=None,
357 )
358 coreconfigitem('experimental', 'evolution.exchange',
359 default=None,
360 )
352 361 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
353 362 default=False,
354 363 alias=[('experimental', 'stabilization.bundle-obsmarker')],
355 364 )
356 365 coreconfigitem('experimental', 'evolution.track-operation',
357 366 default=True,
358 367 alias=[('experimental', 'stabilization.track-operation')]
359 368 )
360 369 coreconfigitem('experimental', 'maxdeltachainspan',
361 370 default=-1,
362 371 )
363 372 coreconfigitem('experimental', 'mmapindexthreshold',
364 373 default=None,
365 374 )
366 375 coreconfigitem('experimental', 'nonnormalparanoidcheck',
367 376 default=False,
368 377 )
369 378 coreconfigitem('experimental', 'effect-flags',
370 379 default=False,
371 380 )
372 381 coreconfigitem('experimental', 'exportableenviron',
373 382 default=list,
374 383 )
375 384 coreconfigitem('experimental', 'extendedheader.index',
376 385 default=None,
377 386 )
378 387 coreconfigitem('experimental', 'extendedheader.similarity',
379 388 default=False,
380 389 )
381 390 coreconfigitem('experimental', 'format.compression',
382 391 default='zlib',
383 392 )
384 393 coreconfigitem('experimental', 'graphshorten',
385 394 default=False,
386 395 )
387 396 coreconfigitem('experimental', 'graphstyle.parent',
388 397 default=dynamicdefault,
389 398 )
390 399 coreconfigitem('experimental', 'graphstyle.missing',
391 400 default=dynamicdefault,
392 401 )
393 402 coreconfigitem('experimental', 'graphstyle.grandparent',
394 403 default=dynamicdefault,
395 404 )
396 405 coreconfigitem('experimental', 'hook-track-tags',
397 406 default=False,
398 407 )
399 408 coreconfigitem('experimental', 'httppostargs',
400 409 default=False,
401 410 )
402 411 coreconfigitem('experimental', 'manifestv2',
403 412 default=False,
404 413 )
405 414 coreconfigitem('experimental', 'mergedriver',
406 415 default=None,
407 416 )
408 417 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
409 418 default=False,
410 419 )
411 420 coreconfigitem('experimental', 'rebase.multidest',
412 421 default=False,
413 422 )
414 423 coreconfigitem('experimental', 'revertalternateinteractivemode',
415 424 default=True,
416 425 )
417 426 coreconfigitem('experimental', 'revlogv2',
418 427 default=None,
419 428 )
420 429 coreconfigitem('experimental', 'spacemovesdown',
421 430 default=False,
422 431 )
423 432 coreconfigitem('experimental', 'sparse-read',
424 433 default=False,
425 434 )
426 435 coreconfigitem('experimental', 'sparse-read.density-threshold',
427 436 default=0.25,
428 437 )
429 438 coreconfigitem('experimental', 'sparse-read.min-block-size',
430 439 default='256K',
431 440 )
432 441 coreconfigitem('experimental', 'treemanifest',
433 442 default=False,
434 443 )
435 444 coreconfigitem('extensions', '.*',
436 445 default=None,
437 446 generic=True,
438 447 )
439 448 coreconfigitem('extdata', '.*',
440 449 default=None,
441 450 generic=True,
442 451 )
443 452 coreconfigitem('format', 'aggressivemergedeltas',
444 453 default=False,
445 454 )
446 455 coreconfigitem('format', 'chunkcachesize',
447 456 default=None,
448 457 )
449 458 coreconfigitem('format', 'dotencode',
450 459 default=True,
451 460 )
452 461 coreconfigitem('format', 'generaldelta',
453 462 default=False,
454 463 )
455 464 coreconfigitem('format', 'manifestcachesize',
456 465 default=None,
457 466 )
458 467 coreconfigitem('format', 'maxchainlen',
459 468 default=None,
460 469 )
461 470 coreconfigitem('format', 'obsstore-version',
462 471 default=None,
463 472 )
464 473 coreconfigitem('format', 'usefncache',
465 474 default=True,
466 475 )
467 476 coreconfigitem('format', 'usegeneraldelta',
468 477 default=True,
469 478 )
470 479 coreconfigitem('format', 'usestore',
471 480 default=True,
472 481 )
473 482 coreconfigitem('hooks', '.*',
474 483 default=dynamicdefault,
475 484 generic=True,
476 485 )
477 486 coreconfigitem('hgweb-paths', '.*',
478 487 default=list,
479 488 generic=True,
480 489 )
481 490 coreconfigitem('hostfingerprints', '.*',
482 491 default=list,
483 492 generic=True,
484 493 )
485 494 coreconfigitem('hostsecurity', 'ciphers',
486 495 default=None,
487 496 )
488 497 coreconfigitem('hostsecurity', 'disabletls10warning',
489 498 default=False,
490 499 )
491 500 coreconfigitem('hostsecurity', 'minimumprotocol',
492 501 default=dynamicdefault,
493 502 )
494 503 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
495 504 default=dynamicdefault,
496 505 generic=True,
497 506 )
498 507 coreconfigitem('hostsecurity', '.*:ciphers$',
499 508 default=dynamicdefault,
500 509 generic=True,
501 510 )
502 511 coreconfigitem('hostsecurity', '.*:fingerprints$',
503 512 default=list,
504 513 generic=True,
505 514 )
506 515 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
507 516 default=None,
508 517 generic=True,
509 518 )
510 519
511 520 coreconfigitem('http_proxy', 'always',
512 521 default=False,
513 522 )
514 523 coreconfigitem('http_proxy', 'host',
515 524 default=None,
516 525 )
517 526 coreconfigitem('http_proxy', 'no',
518 527 default=list,
519 528 )
520 529 coreconfigitem('http_proxy', 'passwd',
521 530 default=None,
522 531 )
523 532 coreconfigitem('http_proxy', 'user',
524 533 default=None,
525 534 )
526 535 coreconfigitem('logtoprocess', 'commandexception',
527 536 default=None,
528 537 )
529 538 coreconfigitem('logtoprocess', 'commandfinish',
530 539 default=None,
531 540 )
532 541 coreconfigitem('logtoprocess', 'command',
533 542 default=None,
534 543 )
535 544 coreconfigitem('logtoprocess', 'develwarn',
536 545 default=None,
537 546 )
538 547 coreconfigitem('logtoprocess', 'uiblocked',
539 548 default=None,
540 549 )
541 550 coreconfigitem('merge', 'checkunknown',
542 551 default='abort',
543 552 )
544 553 coreconfigitem('merge', 'checkignored',
545 554 default='abort',
546 555 )
547 556 coreconfigitem('merge', 'followcopies',
548 557 default=True,
549 558 )
550 559 coreconfigitem('merge', 'on-failure',
551 560 default='continue',
552 561 )
553 562 coreconfigitem('merge', 'preferancestor',
554 563 default=lambda: ['*'],
555 564 )
556 565 coreconfigitem('merge-tools', '.*',
557 566 default=None,
558 567 generic=True,
559 568 )
560 569 coreconfigitem('merge-tools', r'.*\.args$',
561 570 default="$local $base $other",
562 571 generic=True,
563 572 priority=-1,
564 573 )
565 574 coreconfigitem('merge-tools', r'.*\.binary$',
566 575 default=False,
567 576 generic=True,
568 577 priority=-1,
569 578 )
570 579 coreconfigitem('merge-tools', r'.*\.check$',
571 580 default=list,
572 581 generic=True,
573 582 priority=-1,
574 583 )
575 584 coreconfigitem('merge-tools', r'.*\.checkchanged$',
576 585 default=False,
577 586 generic=True,
578 587 priority=-1,
579 588 )
580 589 coreconfigitem('merge-tools', r'.*\.executable$',
581 590 default=dynamicdefault,
582 591 generic=True,
583 592 priority=-1,
584 593 )
585 594 coreconfigitem('merge-tools', r'.*\.fixeol$',
586 595 default=False,
587 596 generic=True,
588 597 priority=-1,
589 598 )
590 599 coreconfigitem('merge-tools', r'.*\.gui$',
591 600 default=False,
592 601 generic=True,
593 602 priority=-1,
594 603 )
595 604 coreconfigitem('merge-tools', r'.*\.priority$',
596 605 default=0,
597 606 generic=True,
598 607 priority=-1,
599 608 )
600 609 coreconfigitem('merge-tools', r'.*\.premerge$',
601 610 default=dynamicdefault,
602 611 generic=True,
603 612 priority=-1,
604 613 )
605 614 coreconfigitem('merge-tools', r'.*\.symlink$',
606 615 default=False,
607 616 generic=True,
608 617 priority=-1,
609 618 )
610 619 coreconfigitem('pager', 'attend-.*',
611 620 default=dynamicdefault,
612 621 generic=True,
613 622 )
614 623 coreconfigitem('pager', 'ignore',
615 624 default=list,
616 625 )
617 626 coreconfigitem('pager', 'pager',
618 627 default=dynamicdefault,
619 628 )
620 629 coreconfigitem('patch', 'eol',
621 630 default='strict',
622 631 )
623 632 coreconfigitem('patch', 'fuzz',
624 633 default=2,
625 634 )
626 635 coreconfigitem('paths', 'default',
627 636 default=None,
628 637 )
629 638 coreconfigitem('paths', 'default-push',
630 639 default=None,
631 640 )
632 641 coreconfigitem('paths', '.*',
633 642 default=None,
634 643 generic=True,
635 644 )
636 645 coreconfigitem('phases', 'checksubrepos',
637 646 default='follow',
638 647 )
639 648 coreconfigitem('phases', 'new-commit',
640 649 default='draft',
641 650 )
642 651 coreconfigitem('phases', 'publish',
643 652 default=True,
644 653 )
645 654 coreconfigitem('profiling', 'enabled',
646 655 default=False,
647 656 )
648 657 coreconfigitem('profiling', 'format',
649 658 default='text',
650 659 )
651 660 coreconfigitem('profiling', 'freq',
652 661 default=1000,
653 662 )
654 663 coreconfigitem('profiling', 'limit',
655 664 default=30,
656 665 )
657 666 coreconfigitem('profiling', 'nested',
658 667 default=0,
659 668 )
660 669 coreconfigitem('profiling', 'output',
661 670 default=None,
662 671 )
663 672 coreconfigitem('profiling', 'showmax',
664 673 default=0.999,
665 674 )
666 675 coreconfigitem('profiling', 'showmin',
667 676 default=dynamicdefault,
668 677 )
669 678 coreconfigitem('profiling', 'sort',
670 679 default='inlinetime',
671 680 )
672 681 coreconfigitem('profiling', 'statformat',
673 682 default='hotpath',
674 683 )
675 684 coreconfigitem('profiling', 'type',
676 685 default='stat',
677 686 )
678 687 coreconfigitem('progress', 'assume-tty',
679 688 default=False,
680 689 )
681 690 coreconfigitem('progress', 'changedelay',
682 691 default=1,
683 692 )
684 693 coreconfigitem('progress', 'clear-complete',
685 694 default=True,
686 695 )
687 696 coreconfigitem('progress', 'debug',
688 697 default=False,
689 698 )
690 699 coreconfigitem('progress', 'delay',
691 700 default=3,
692 701 )
693 702 coreconfigitem('progress', 'disable',
694 703 default=False,
695 704 )
696 705 coreconfigitem('progress', 'estimateinterval',
697 706 default=60.0,
698 707 )
699 708 coreconfigitem('progress', 'format',
700 709 default=lambda: ['topic', 'bar', 'number', 'estimate'],
701 710 )
702 711 coreconfigitem('progress', 'refresh',
703 712 default=0.1,
704 713 )
705 714 coreconfigitem('progress', 'width',
706 715 default=dynamicdefault,
707 716 )
708 717 coreconfigitem('push', 'pushvars.server',
709 718 default=False,
710 719 )
711 720 coreconfigitem('server', 'bundle1',
712 721 default=True,
713 722 )
714 723 coreconfigitem('server', 'bundle1gd',
715 724 default=None,
716 725 )
717 726 coreconfigitem('server', 'bundle1.pull',
718 727 default=None,
719 728 )
720 729 coreconfigitem('server', 'bundle1gd.pull',
721 730 default=None,
722 731 )
723 732 coreconfigitem('server', 'bundle1.push',
724 733 default=None,
725 734 )
726 735 coreconfigitem('server', 'bundle1gd.push',
727 736 default=None,
728 737 )
729 738 coreconfigitem('server', 'compressionengines',
730 739 default=list,
731 740 )
732 741 coreconfigitem('server', 'concurrent-push-mode',
733 742 default='strict',
734 743 )
735 744 coreconfigitem('server', 'disablefullbundle',
736 745 default=False,
737 746 )
738 747 coreconfigitem('server', 'maxhttpheaderlen',
739 748 default=1024,
740 749 )
741 750 coreconfigitem('server', 'preferuncompressed',
742 751 default=False,
743 752 )
744 753 coreconfigitem('server', 'uncompressed',
745 754 default=True,
746 755 )
747 756 coreconfigitem('server', 'uncompressedallowsecret',
748 757 default=False,
749 758 )
750 759 coreconfigitem('server', 'validate',
751 760 default=False,
752 761 )
753 762 coreconfigitem('server', 'zliblevel',
754 763 default=-1,
755 764 )
756 765 coreconfigitem('smtp', 'host',
757 766 default=None,
758 767 )
759 768 coreconfigitem('smtp', 'local_hostname',
760 769 default=None,
761 770 )
762 771 coreconfigitem('smtp', 'password',
763 772 default=None,
764 773 )
765 774 coreconfigitem('smtp', 'port',
766 775 default=dynamicdefault,
767 776 )
768 777 coreconfigitem('smtp', 'tls',
769 778 default='none',
770 779 )
771 780 coreconfigitem('smtp', 'username',
772 781 default=None,
773 782 )
774 783 coreconfigitem('sparse', 'missingwarning',
775 784 default=True,
776 785 )
777 786 coreconfigitem('templates', '.*',
778 787 default=None,
779 788 generic=True,
780 789 )
781 790 coreconfigitem('trusted', 'groups',
782 791 default=list,
783 792 )
784 793 coreconfigitem('trusted', 'users',
785 794 default=list,
786 795 )
787 796 coreconfigitem('ui', '_usedassubrepo',
788 797 default=False,
789 798 )
790 799 coreconfigitem('ui', 'allowemptycommit',
791 800 default=False,
792 801 )
793 802 coreconfigitem('ui', 'archivemeta',
794 803 default=True,
795 804 )
796 805 coreconfigitem('ui', 'askusername',
797 806 default=False,
798 807 )
799 808 coreconfigitem('ui', 'clonebundlefallback',
800 809 default=False,
801 810 )
802 811 coreconfigitem('ui', 'clonebundleprefers',
803 812 default=list,
804 813 )
805 814 coreconfigitem('ui', 'clonebundles',
806 815 default=True,
807 816 )
808 817 coreconfigitem('ui', 'color',
809 818 default='auto',
810 819 )
811 820 coreconfigitem('ui', 'commitsubrepos',
812 821 default=False,
813 822 )
814 823 coreconfigitem('ui', 'debug',
815 824 default=False,
816 825 )
817 826 coreconfigitem('ui', 'debugger',
818 827 default=None,
819 828 )
820 829 coreconfigitem('ui', 'fallbackencoding',
821 830 default=None,
822 831 )
823 832 coreconfigitem('ui', 'forcecwd',
824 833 default=None,
825 834 )
826 835 coreconfigitem('ui', 'forcemerge',
827 836 default=None,
828 837 )
829 838 coreconfigitem('ui', 'formatdebug',
830 839 default=False,
831 840 )
832 841 coreconfigitem('ui', 'formatjson',
833 842 default=False,
834 843 )
835 844 coreconfigitem('ui', 'formatted',
836 845 default=None,
837 846 )
838 847 coreconfigitem('ui', 'graphnodetemplate',
839 848 default=None,
840 849 )
841 850 coreconfigitem('ui', 'http2debuglevel',
842 851 default=None,
843 852 )
844 853 coreconfigitem('ui', 'interactive',
845 854 default=None,
846 855 )
847 856 coreconfigitem('ui', 'interface',
848 857 default=None,
849 858 )
850 859 coreconfigitem('ui', 'interface.chunkselector',
851 860 default=None,
852 861 )
853 862 coreconfigitem('ui', 'logblockedtimes',
854 863 default=False,
855 864 )
856 865 coreconfigitem('ui', 'logtemplate',
857 866 default=None,
858 867 )
859 868 coreconfigitem('ui', 'merge',
860 869 default=None,
861 870 )
862 871 coreconfigitem('ui', 'mergemarkers',
863 872 default='basic',
864 873 )
865 874 coreconfigitem('ui', 'mergemarkertemplate',
866 875 default=('{node|short} '
867 876 '{ifeq(tags, "tip", "", '
868 877 'ifeq(tags, "", "", "{tags} "))}'
869 878 '{if(bookmarks, "{bookmarks} ")}'
870 879 '{ifeq(branch, "default", "", "{branch} ")}'
871 880 '- {author|user}: {desc|firstline}')
872 881 )
873 882 coreconfigitem('ui', 'nontty',
874 883 default=False,
875 884 )
876 885 coreconfigitem('ui', 'origbackuppath',
877 886 default=None,
878 887 )
879 888 coreconfigitem('ui', 'paginate',
880 889 default=True,
881 890 )
882 891 coreconfigitem('ui', 'patch',
883 892 default=None,
884 893 )
885 894 coreconfigitem('ui', 'portablefilenames',
886 895 default='warn',
887 896 )
888 897 coreconfigitem('ui', 'promptecho',
889 898 default=False,
890 899 )
891 900 coreconfigitem('ui', 'quiet',
892 901 default=False,
893 902 )
894 903 coreconfigitem('ui', 'quietbookmarkmove',
895 904 default=False,
896 905 )
897 906 coreconfigitem('ui', 'remotecmd',
898 907 default='hg',
899 908 )
900 909 coreconfigitem('ui', 'report_untrusted',
901 910 default=True,
902 911 )
903 912 coreconfigitem('ui', 'rollback',
904 913 default=True,
905 914 )
906 915 coreconfigitem('ui', 'slash',
907 916 default=False,
908 917 )
909 918 coreconfigitem('ui', 'ssh',
910 919 default='ssh',
911 920 )
912 921 coreconfigitem('ui', 'statuscopies',
913 922 default=False,
914 923 )
915 924 coreconfigitem('ui', 'strict',
916 925 default=False,
917 926 )
918 927 coreconfigitem('ui', 'style',
919 928 default='',
920 929 )
921 930 coreconfigitem('ui', 'supportcontact',
922 931 default=None,
923 932 )
924 933 coreconfigitem('ui', 'textwidth',
925 934 default=78,
926 935 )
927 936 coreconfigitem('ui', 'timeout',
928 937 default='600',
929 938 )
930 939 coreconfigitem('ui', 'traceback',
931 940 default=False,
932 941 )
933 942 coreconfigitem('ui', 'tweakdefaults',
934 943 default=False,
935 944 )
936 945 coreconfigitem('ui', 'usehttp2',
937 946 default=False,
938 947 )
939 948 coreconfigitem('ui', 'username',
940 949 alias=[('ui', 'user')]
941 950 )
942 951 coreconfigitem('ui', 'verbose',
943 952 default=False,
944 953 )
945 954 coreconfigitem('verify', 'skipflags',
946 955 default=None,
947 956 )
948 957 coreconfigitem('web', 'allowbz2',
949 958 default=False,
950 959 )
951 960 coreconfigitem('web', 'allowgz',
952 961 default=False,
953 962 )
954 963 coreconfigitem('web', 'allowpull',
955 964 default=True,
956 965 )
957 966 coreconfigitem('web', 'allow_push',
958 967 default=list,
959 968 )
960 969 coreconfigitem('web', 'allowzip',
961 970 default=False,
962 971 )
963 972 coreconfigitem('web', 'archivesubrepos',
964 973 default=False,
965 974 )
966 975 coreconfigitem('web', 'cache',
967 976 default=True,
968 977 )
969 978 coreconfigitem('web', 'contact',
970 979 default=None,
971 980 )
972 981 coreconfigitem('web', 'deny_push',
973 982 default=list,
974 983 )
975 984 coreconfigitem('web', 'guessmime',
976 985 default=False,
977 986 )
978 987 coreconfigitem('web', 'hidden',
979 988 default=False,
980 989 )
981 990 coreconfigitem('web', 'labels',
982 991 default=list,
983 992 )
984 993 coreconfigitem('web', 'logoimg',
985 994 default='hglogo.png',
986 995 )
987 996 coreconfigitem('web', 'logourl',
988 997 default='https://mercurial-scm.org/',
989 998 )
990 999 coreconfigitem('web', 'accesslog',
991 1000 default='-',
992 1001 )
993 1002 coreconfigitem('web', 'address',
994 1003 default='',
995 1004 )
996 1005 coreconfigitem('web', 'allow_archive',
997 1006 default=list,
998 1007 )
999 1008 coreconfigitem('web', 'allow_read',
1000 1009 default=list,
1001 1010 )
1002 1011 coreconfigitem('web', 'baseurl',
1003 1012 default=None,
1004 1013 )
1005 1014 coreconfigitem('web', 'cacerts',
1006 1015 default=None,
1007 1016 )
1008 1017 coreconfigitem('web', 'certificate',
1009 1018 default=None,
1010 1019 )
1011 1020 coreconfigitem('web', 'collapse',
1012 1021 default=False,
1013 1022 )
1014 1023 coreconfigitem('web', 'csp',
1015 1024 default=None,
1016 1025 )
1017 1026 coreconfigitem('web', 'deny_read',
1018 1027 default=list,
1019 1028 )
1020 1029 coreconfigitem('web', 'descend',
1021 1030 default=True,
1022 1031 )
1023 1032 coreconfigitem('web', 'description',
1024 1033 default="",
1025 1034 )
1026 1035 coreconfigitem('web', 'encoding',
1027 1036 default=lambda: encoding.encoding,
1028 1037 )
1029 1038 coreconfigitem('web', 'errorlog',
1030 1039 default='-',
1031 1040 )
1032 1041 coreconfigitem('web', 'ipv6',
1033 1042 default=False,
1034 1043 )
1035 1044 coreconfigitem('web', 'maxchanges',
1036 1045 default=10,
1037 1046 )
1038 1047 coreconfigitem('web', 'maxfiles',
1039 1048 default=10,
1040 1049 )
1041 1050 coreconfigitem('web', 'maxshortchanges',
1042 1051 default=60,
1043 1052 )
1044 1053 coreconfigitem('web', 'motd',
1045 1054 default='',
1046 1055 )
1047 1056 coreconfigitem('web', 'name',
1048 1057 default=dynamicdefault,
1049 1058 )
1050 1059 coreconfigitem('web', 'port',
1051 1060 default=8000,
1052 1061 )
1053 1062 coreconfigitem('web', 'prefix',
1054 1063 default='',
1055 1064 )
1056 1065 coreconfigitem('web', 'push_ssl',
1057 1066 default=True,
1058 1067 )
1059 1068 coreconfigitem('web', 'refreshinterval',
1060 1069 default=20,
1061 1070 )
1062 1071 coreconfigitem('web', 'staticurl',
1063 1072 default=None,
1064 1073 )
1065 1074 coreconfigitem('web', 'stripes',
1066 1075 default=1,
1067 1076 )
1068 1077 coreconfigitem('web', 'style',
1069 1078 default='paper',
1070 1079 )
1071 1080 coreconfigitem('web', 'templates',
1072 1081 default=None,
1073 1082 )
1074 1083 coreconfigitem('web', 'view',
1075 1084 default='served',
1076 1085 )
1077 1086 coreconfigitem('worker', 'backgroundclose',
1078 1087 default=dynamicdefault,
1079 1088 )
1080 1089 # Windows defaults to a limit of 512 open files. A buffer of 128
1081 1090 # should give us enough headway.
1082 1091 coreconfigitem('worker', 'backgroundclosemaxqueue',
1083 1092 default=384,
1084 1093 )
1085 1094 coreconfigitem('worker', 'backgroundcloseminfilecount',
1086 1095 default=2048,
1087 1096 )
1088 1097 coreconfigitem('worker', 'backgroundclosethreadcount',
1089 1098 default=4,
1090 1099 )
1091 1100 coreconfigitem('worker', 'numcpus',
1092 1101 default=None,
1093 1102 )
1094 1103
1095 1104 # Rebase related configuration moved to core because other extension are doing
1096 1105 # strange things. For example, shelve import the extensions to reuse some bit
1097 1106 # without formally loading it.
1098 1107 coreconfigitem('commands', 'rebase.requiredest',
1099 1108 default=False,
1100 1109 )
1101 1110 coreconfigitem('experimental', 'rebaseskipobsolete',
1102 1111 default=True,
1103 1112 )
1104 1113 coreconfigitem('rebase', 'singletransaction',
1105 1114 default=False,
1106 1115 )
@@ -1,1098 +1,1126 b''
1 1 # obsolete.py - obsolete markers handling
2 2 #
3 3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 4 # Logilab SA <contact@logilab.fr>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 """Obsolete marker handling
10 10
11 11 An obsolete marker maps an old changeset to a list of new
12 12 changesets. If the list of new changesets is empty, the old changeset
13 13 is said to be "killed". Otherwise, the old changeset is being
14 14 "replaced" by the new changesets.
15 15
16 16 Obsolete markers can be used to record and distribute changeset graph
17 17 transformations performed by history rewrite operations, and help
18 18 building new tools to reconcile conflicting rewrite actions. To
19 19 facilitate conflict resolution, markers include various annotations
20 20 besides old and news changeset identifiers, such as creation date or
21 21 author name.
22 22
23 23 The old obsoleted changeset is called a "predecessor" and possible
24 24 replacements are called "successors". Markers that used changeset X as
25 25 a predecessor are called "successor markers of X" because they hold
26 26 information about the successors of X. Markers that use changeset Y as
27 27 a successors are call "predecessor markers of Y" because they hold
28 28 information about the predecessors of Y.
29 29
30 30 Examples:
31 31
32 32 - When changeset A is replaced by changeset A', one marker is stored:
33 33
34 34 (A, (A',))
35 35
36 36 - When changesets A and B are folded into a new changeset C, two markers are
37 37 stored:
38 38
39 39 (A, (C,)) and (B, (C,))
40 40
41 41 - When changeset A is simply "pruned" from the graph, a marker is created:
42 42
43 43 (A, ())
44 44
45 45 - When changeset A is split into B and C, a single marker is used:
46 46
47 47 (A, (B, C))
48 48
49 49 We use a single marker to distinguish the "split" case from the "divergence"
50 50 case. If two independent operations rewrite the same changeset A in to A' and
51 51 A'', we have an error case: divergent rewriting. We can detect it because
52 52 two markers will be created independently:
53 53
54 54 (A, (B,)) and (A, (C,))
55 55
56 56 Format
57 57 ------
58 58
59 59 Markers are stored in an append-only file stored in
60 60 '.hg/store/obsstore'.
61 61
62 62 The file starts with a version header:
63 63
64 64 - 1 unsigned byte: version number, starting at zero.
65 65
66 66 The header is followed by the markers. Marker format depend of the version. See
67 67 comment associated with each format for details.
68 68
69 69 """
70 70 from __future__ import absolute_import
71 71
72 72 import errno
73 73 import struct
74 74
75 75 from .i18n import _
76 76 from . import (
77 77 error,
78 78 node,
79 79 obsutil,
80 80 phases,
81 81 policy,
82 82 util,
83 83 )
84 84
85 85 parsers = policy.importmod(r'parsers')
86 86
87 87 _pack = struct.pack
88 88 _unpack = struct.unpack
89 89 _calcsize = struct.calcsize
90 90 propertycache = util.propertycache
91 91
92 92 # the obsolete feature is not mature enough to be enabled by default.
93 93 # you have to rely on third party extension extension to enable this.
94 94 _enabled = False
95 95
96 96 # Options for obsolescence
97 97 createmarkersopt = 'createmarkers'
98 98 allowunstableopt = 'allowunstable'
99 99 exchangeopt = 'exchange'
100 100
101 def _getoptionvalue(repo, option):
102 """Returns True if the given repository has the given obsolete option
103 enabled.
104 """
105 configkey = 'evolution.%s' % option
106 newconfig = repo.ui.configbool('experimental', configkey)
107
108 # Return the value only if defined
109 if newconfig is not None:
110 return newconfig
111
112 # Fallback on generic option
113 try:
114 return repo.ui.configbool('experimental', 'evolution')
115 except (error.ConfigError, AttributeError):
116 # Fallback on old-fashion config
117 # inconsistent config: experimental.evolution
118 result = set(repo.ui.configlist('experimental', 'evolution'))
119
120 if 'all' in result:
121 return True
122
123 # For migration purposes, temporarily return true if the config hasn't
124 # been set but _enabled is true.
125 if len(result) == 0 and _enabled:
126 return True
127
128 # Temporary hack for next check
129 newconfig = repo.ui.config('experimental', 'evolution.createmarkers')
130 if newconfig:
131 result.add('createmarkers')
132
133 return option in result
134
101 135 def isenabled(repo, option):
102 136 """Returns True if the given repository has the given obsolete option
103 137 enabled.
104 138 """
105 result = set(repo.ui.configlist('experimental', 'evolution'))
106 if 'all' in result:
107 return True
108
109 # For migration purposes, temporarily return true if the config hasn't been
110 # set but _enabled is true.
111 if len(result) == 0 and _enabled:
112 return True
139 createmarkersvalue = _getoptionvalue(repo, createmarkersopt)
140 unstabluevalue = _getoptionvalue(repo, allowunstableopt)
141 exchangevalue = _getoptionvalue(repo, exchangeopt)
113 142
114 143 # createmarkers must be enabled if other options are enabled
115 if ((allowunstableopt in result or exchangeopt in result) and
116 not createmarkersopt in result):
144 if ((unstabluevalue or exchangevalue) and not createmarkersvalue):
117 145 raise error.Abort(_("'createmarkers' obsolete option must be enabled "
118 "if other obsolete options are enabled"))
146 "if other obsolete options are enabled"))
119 147
120 return option in result
148 return _getoptionvalue(repo, option)
121 149
122 150 ### obsolescence marker flag
123 151
124 152 ## bumpedfix flag
125 153 #
126 154 # When a changeset A' succeed to a changeset A which became public, we call A'
127 155 # "bumped" because it's a successors of a public changesets
128 156 #
129 157 # o A' (bumped)
130 158 # |`:
131 159 # | o A
132 160 # |/
133 161 # o Z
134 162 #
135 163 # The way to solve this situation is to create a new changeset Ad as children
136 164 # of A. This changeset have the same content than A'. So the diff from A to A'
137 165 # is the same than the diff from A to Ad. Ad is marked as a successors of A'
138 166 #
139 167 # o Ad
140 168 # |`:
141 169 # | x A'
142 170 # |'|
143 171 # o | A
144 172 # |/
145 173 # o Z
146 174 #
147 175 # But by transitivity Ad is also a successors of A. To avoid having Ad marked
148 176 # as bumped too, we add the `bumpedfix` flag to the marker. <A', (Ad,)>.
149 177 # This flag mean that the successors express the changes between the public and
150 178 # bumped version and fix the situation, breaking the transitivity of
151 179 # "bumped" here.
152 180 bumpedfix = 1
153 181 usingsha256 = 2
154 182
155 183 ## Parsing and writing of version "0"
156 184 #
157 185 # The header is followed by the markers. Each marker is made of:
158 186 #
159 187 # - 1 uint8 : number of new changesets "N", can be zero.
160 188 #
161 189 # - 1 uint32: metadata size "M" in bytes.
162 190 #
163 191 # - 1 byte: a bit field. It is reserved for flags used in common
164 192 # obsolete marker operations, to avoid repeated decoding of metadata
165 193 # entries.
166 194 #
167 195 # - 20 bytes: obsoleted changeset identifier.
168 196 #
169 197 # - N*20 bytes: new changesets identifiers.
170 198 #
171 199 # - M bytes: metadata as a sequence of nul-terminated strings. Each
172 200 # string contains a key and a value, separated by a colon ':', without
173 201 # additional encoding. Keys cannot contain '\0' or ':' and values
174 202 # cannot contain '\0'.
175 203 _fm0version = 0
176 204 _fm0fixed = '>BIB20s'
177 205 _fm0node = '20s'
178 206 _fm0fsize = _calcsize(_fm0fixed)
179 207 _fm0fnodesize = _calcsize(_fm0node)
180 208
181 209 def _fm0readmarkers(data, off, stop):
182 210 # Loop on markers
183 211 while off < stop:
184 212 # read fixed part
185 213 cur = data[off:off + _fm0fsize]
186 214 off += _fm0fsize
187 215 numsuc, mdsize, flags, pre = _unpack(_fm0fixed, cur)
188 216 # read replacement
189 217 sucs = ()
190 218 if numsuc:
191 219 s = (_fm0fnodesize * numsuc)
192 220 cur = data[off:off + s]
193 221 sucs = _unpack(_fm0node * numsuc, cur)
194 222 off += s
195 223 # read metadata
196 224 # (metadata will be decoded on demand)
197 225 metadata = data[off:off + mdsize]
198 226 if len(metadata) != mdsize:
199 227 raise error.Abort(_('parsing obsolete marker: metadata is too '
200 228 'short, %d bytes expected, got %d')
201 229 % (mdsize, len(metadata)))
202 230 off += mdsize
203 231 metadata = _fm0decodemeta(metadata)
204 232 try:
205 233 when, offset = metadata.pop('date', '0 0').split(' ')
206 234 date = float(when), int(offset)
207 235 except ValueError:
208 236 date = (0., 0)
209 237 parents = None
210 238 if 'p2' in metadata:
211 239 parents = (metadata.pop('p1', None), metadata.pop('p2', None))
212 240 elif 'p1' in metadata:
213 241 parents = (metadata.pop('p1', None),)
214 242 elif 'p0' in metadata:
215 243 parents = ()
216 244 if parents is not None:
217 245 try:
218 246 parents = tuple(node.bin(p) for p in parents)
219 247 # if parent content is not a nodeid, drop the data
220 248 for p in parents:
221 249 if len(p) != 20:
222 250 parents = None
223 251 break
224 252 except TypeError:
225 253 # if content cannot be translated to nodeid drop the data.
226 254 parents = None
227 255
228 256 metadata = tuple(sorted(metadata.iteritems()))
229 257
230 258 yield (pre, sucs, flags, metadata, date, parents)
231 259
232 260 def _fm0encodeonemarker(marker):
233 261 pre, sucs, flags, metadata, date, parents = marker
234 262 if flags & usingsha256:
235 263 raise error.Abort(_('cannot handle sha256 with old obsstore format'))
236 264 metadata = dict(metadata)
237 265 time, tz = date
238 266 metadata['date'] = '%r %i' % (time, tz)
239 267 if parents is not None:
240 268 if not parents:
241 269 # mark that we explicitly recorded no parents
242 270 metadata['p0'] = ''
243 271 for i, p in enumerate(parents, 1):
244 272 metadata['p%i' % i] = node.hex(p)
245 273 metadata = _fm0encodemeta(metadata)
246 274 numsuc = len(sucs)
247 275 format = _fm0fixed + (_fm0node * numsuc)
248 276 data = [numsuc, len(metadata), flags, pre]
249 277 data.extend(sucs)
250 278 return _pack(format, *data) + metadata
251 279
252 280 def _fm0encodemeta(meta):
253 281 """Return encoded metadata string to string mapping.
254 282
255 283 Assume no ':' in key and no '\0' in both key and value."""
256 284 for key, value in meta.iteritems():
257 285 if ':' in key or '\0' in key:
258 286 raise ValueError("':' and '\0' are forbidden in metadata key'")
259 287 if '\0' in value:
260 288 raise ValueError("':' is forbidden in metadata value'")
261 289 return '\0'.join(['%s:%s' % (k, meta[k]) for k in sorted(meta)])
262 290
263 291 def _fm0decodemeta(data):
264 292 """Return string to string dictionary from encoded version."""
265 293 d = {}
266 294 for l in data.split('\0'):
267 295 if l:
268 296 key, value = l.split(':')
269 297 d[key] = value
270 298 return d
271 299
272 300 ## Parsing and writing of version "1"
273 301 #
274 302 # The header is followed by the markers. Each marker is made of:
275 303 #
276 304 # - uint32: total size of the marker (including this field)
277 305 #
278 306 # - float64: date in seconds since epoch
279 307 #
280 308 # - int16: timezone offset in minutes
281 309 #
282 310 # - uint16: a bit field. It is reserved for flags used in common
283 311 # obsolete marker operations, to avoid repeated decoding of metadata
284 312 # entries.
285 313 #
286 314 # - uint8: number of successors "N", can be zero.
287 315 #
288 316 # - uint8: number of parents "P", can be zero.
289 317 #
290 318 # 0: parents data stored but no parent,
291 319 # 1: one parent stored,
292 320 # 2: two parents stored,
293 321 # 3: no parent data stored
294 322 #
295 323 # - uint8: number of metadata entries M
296 324 #
297 325 # - 20 or 32 bytes: predecessor changeset identifier.
298 326 #
299 327 # - N*(20 or 32) bytes: successors changesets identifiers.
300 328 #
301 329 # - P*(20 or 32) bytes: parents of the predecessors changesets.
302 330 #
303 331 # - M*(uint8, uint8): size of all metadata entries (key and value)
304 332 #
305 333 # - remaining bytes: the metadata, each (key, value) pair after the other.
306 334 _fm1version = 1
307 335 _fm1fixed = '>IdhHBBB20s'
308 336 _fm1nodesha1 = '20s'
309 337 _fm1nodesha256 = '32s'
310 338 _fm1nodesha1size = _calcsize(_fm1nodesha1)
311 339 _fm1nodesha256size = _calcsize(_fm1nodesha256)
312 340 _fm1fsize = _calcsize(_fm1fixed)
313 341 _fm1parentnone = 3
314 342 _fm1parentshift = 14
315 343 _fm1parentmask = (_fm1parentnone << _fm1parentshift)
316 344 _fm1metapair = 'BB'
317 345 _fm1metapairsize = _calcsize(_fm1metapair)
318 346
319 347 def _fm1purereadmarkers(data, off, stop):
320 348 # make some global constants local for performance
321 349 noneflag = _fm1parentnone
322 350 sha2flag = usingsha256
323 351 sha1size = _fm1nodesha1size
324 352 sha2size = _fm1nodesha256size
325 353 sha1fmt = _fm1nodesha1
326 354 sha2fmt = _fm1nodesha256
327 355 metasize = _fm1metapairsize
328 356 metafmt = _fm1metapair
329 357 fsize = _fm1fsize
330 358 unpack = _unpack
331 359
332 360 # Loop on markers
333 361 ufixed = struct.Struct(_fm1fixed).unpack
334 362
335 363 while off < stop:
336 364 # read fixed part
337 365 o1 = off + fsize
338 366 t, secs, tz, flags, numsuc, numpar, nummeta, prec = ufixed(data[off:o1])
339 367
340 368 if flags & sha2flag:
341 369 # FIXME: prec was read as a SHA1, needs to be amended
342 370
343 371 # read 0 or more successors
344 372 if numsuc == 1:
345 373 o2 = o1 + sha2size
346 374 sucs = (data[o1:o2],)
347 375 else:
348 376 o2 = o1 + sha2size * numsuc
349 377 sucs = unpack(sha2fmt * numsuc, data[o1:o2])
350 378
351 379 # read parents
352 380 if numpar == noneflag:
353 381 o3 = o2
354 382 parents = None
355 383 elif numpar == 1:
356 384 o3 = o2 + sha2size
357 385 parents = (data[o2:o3],)
358 386 else:
359 387 o3 = o2 + sha2size * numpar
360 388 parents = unpack(sha2fmt * numpar, data[o2:o3])
361 389 else:
362 390 # read 0 or more successors
363 391 if numsuc == 1:
364 392 o2 = o1 + sha1size
365 393 sucs = (data[o1:o2],)
366 394 else:
367 395 o2 = o1 + sha1size * numsuc
368 396 sucs = unpack(sha1fmt * numsuc, data[o1:o2])
369 397
370 398 # read parents
371 399 if numpar == noneflag:
372 400 o3 = o2
373 401 parents = None
374 402 elif numpar == 1:
375 403 o3 = o2 + sha1size
376 404 parents = (data[o2:o3],)
377 405 else:
378 406 o3 = o2 + sha1size * numpar
379 407 parents = unpack(sha1fmt * numpar, data[o2:o3])
380 408
381 409 # read metadata
382 410 off = o3 + metasize * nummeta
383 411 metapairsize = unpack('>' + (metafmt * nummeta), data[o3:off])
384 412 metadata = []
385 413 for idx in xrange(0, len(metapairsize), 2):
386 414 o1 = off + metapairsize[idx]
387 415 o2 = o1 + metapairsize[idx + 1]
388 416 metadata.append((data[off:o1], data[o1:o2]))
389 417 off = o2
390 418
391 419 yield (prec, sucs, flags, tuple(metadata), (secs, tz * 60), parents)
392 420
393 421 def _fm1encodeonemarker(marker):
394 422 pre, sucs, flags, metadata, date, parents = marker
395 423 # determine node size
396 424 _fm1node = _fm1nodesha1
397 425 if flags & usingsha256:
398 426 _fm1node = _fm1nodesha256
399 427 numsuc = len(sucs)
400 428 numextranodes = numsuc
401 429 if parents is None:
402 430 numpar = _fm1parentnone
403 431 else:
404 432 numpar = len(parents)
405 433 numextranodes += numpar
406 434 formatnodes = _fm1node * numextranodes
407 435 formatmeta = _fm1metapair * len(metadata)
408 436 format = _fm1fixed + formatnodes + formatmeta
409 437 # tz is stored in minutes so we divide by 60
410 438 tz = date[1]//60
411 439 data = [None, date[0], tz, flags, numsuc, numpar, len(metadata), pre]
412 440 data.extend(sucs)
413 441 if parents is not None:
414 442 data.extend(parents)
415 443 totalsize = _calcsize(format)
416 444 for key, value in metadata:
417 445 lk = len(key)
418 446 lv = len(value)
419 447 if lk > 255:
420 448 msg = ('obsstore metadata key cannot be longer than 255 bytes'
421 449 ' (key "%s" is %u bytes)') % (key, lk)
422 450 raise error.ProgrammingError(msg)
423 451 if lv > 255:
424 452 msg = ('obsstore metadata value cannot be longer than 255 bytes'
425 453 ' (value "%s" for key "%s" is %u bytes)') % (value, key, lv)
426 454 raise error.ProgrammingError(msg)
427 455 data.append(lk)
428 456 data.append(lv)
429 457 totalsize += lk + lv
430 458 data[0] = totalsize
431 459 data = [_pack(format, *data)]
432 460 for key, value in metadata:
433 461 data.append(key)
434 462 data.append(value)
435 463 return ''.join(data)
436 464
437 465 def _fm1readmarkers(data, off, stop):
438 466 native = getattr(parsers, 'fm1readmarkers', None)
439 467 if not native:
440 468 return _fm1purereadmarkers(data, off, stop)
441 469 return native(data, off, stop)
442 470
443 471 # mapping to read/write various marker formats
444 472 # <version> -> (decoder, encoder)
445 473 formats = {_fm0version: (_fm0readmarkers, _fm0encodeonemarker),
446 474 _fm1version: (_fm1readmarkers, _fm1encodeonemarker)}
447 475
448 476 def _readmarkerversion(data):
449 477 return _unpack('>B', data[0:1])[0]
450 478
451 479 @util.nogc
452 480 def _readmarkers(data, off=None, stop=None):
453 481 """Read and enumerate markers from raw data"""
454 482 diskversion = _readmarkerversion(data)
455 483 if not off:
456 484 off = 1 # skip 1 byte version number
457 485 if stop is None:
458 486 stop = len(data)
459 487 if diskversion not in formats:
460 488 msg = _('parsing obsolete marker: unknown version %r') % diskversion
461 489 raise error.UnknownVersion(msg, version=diskversion)
462 490 return diskversion, formats[diskversion][0](data, off, stop)
463 491
464 492 def encodeheader(version=_fm0version):
465 493 return _pack('>B', version)
466 494
467 495 def encodemarkers(markers, addheader=False, version=_fm0version):
468 496 # Kept separate from flushmarkers(), it will be reused for
469 497 # markers exchange.
470 498 encodeone = formats[version][1]
471 499 if addheader:
472 500 yield encodeheader(version)
473 501 for marker in markers:
474 502 yield encodeone(marker)
475 503
476 504 @util.nogc
477 505 def _addsuccessors(successors, markers):
478 506 for mark in markers:
479 507 successors.setdefault(mark[0], set()).add(mark)
480 508
481 509 def _addprecursors(*args, **kwargs):
482 510 msg = ("'obsolete._addprecursors' is deprecated, "
483 511 "use 'obsolete._addpredecessors'")
484 512 util.nouideprecwarn(msg, '4.4')
485 513
486 514 return _addpredecessors(*args, **kwargs)
487 515
488 516 @util.nogc
489 517 def _addpredecessors(predecessors, markers):
490 518 for mark in markers:
491 519 for suc in mark[1]:
492 520 predecessors.setdefault(suc, set()).add(mark)
493 521
494 522 @util.nogc
495 523 def _addchildren(children, markers):
496 524 for mark in markers:
497 525 parents = mark[5]
498 526 if parents is not None:
499 527 for p in parents:
500 528 children.setdefault(p, set()).add(mark)
501 529
502 530 def _checkinvalidmarkers(markers):
503 531 """search for marker with invalid data and raise error if needed
504 532
505 533 Exist as a separated function to allow the evolve extension for a more
506 534 subtle handling.
507 535 """
508 536 for mark in markers:
509 537 if node.nullid in mark[1]:
510 538 raise error.Abort(_('bad obsolescence marker detected: '
511 539 'invalid successors nullid'))
512 540
513 541 class obsstore(object):
514 542 """Store obsolete markers
515 543
516 544 Markers can be accessed with two mappings:
517 545 - predecessors[x] -> set(markers on predecessors edges of x)
518 546 - successors[x] -> set(markers on successors edges of x)
519 547 - children[x] -> set(markers on predecessors edges of children(x)
520 548 """
521 549
522 550 fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents')
523 551 # prec: nodeid, predecessors changesets
524 552 # succs: tuple of nodeid, successor changesets (0-N length)
525 553 # flag: integer, flag field carrying modifier for the markers (see doc)
526 554 # meta: binary blob, encoded metadata dictionary
527 555 # date: (float, int) tuple, date of marker creation
528 556 # parents: (tuple of nodeid) or None, parents of predecessors
529 557 # None is used when no data has been recorded
530 558
531 559 def __init__(self, svfs, defaultformat=_fm1version, readonly=False):
532 560 # caches for various obsolescence related cache
533 561 self.caches = {}
534 562 self.svfs = svfs
535 563 self._defaultformat = defaultformat
536 564 self._readonly = readonly
537 565
538 566 def __iter__(self):
539 567 return iter(self._all)
540 568
541 569 def __len__(self):
542 570 return len(self._all)
543 571
544 572 def __nonzero__(self):
545 573 if not self._cached('_all'):
546 574 try:
547 575 return self.svfs.stat('obsstore').st_size > 1
548 576 except OSError as inst:
549 577 if inst.errno != errno.ENOENT:
550 578 raise
551 579 # just build an empty _all list if no obsstore exists, which
552 580 # avoids further stat() syscalls
553 581 return bool(self._all)
554 582
555 583 __bool__ = __nonzero__
556 584
557 585 @property
558 586 def readonly(self):
559 587 """True if marker creation is disabled
560 588
561 589 Remove me in the future when obsolete marker is always on."""
562 590 return self._readonly
563 591
564 592 def create(self, transaction, prec, succs=(), flag=0, parents=None,
565 593 date=None, metadata=None, ui=None):
566 594 """obsolete: add a new obsolete marker
567 595
568 596 * ensuring it is hashable
569 597 * check mandatory metadata
570 598 * encode metadata
571 599
572 600 If you are a human writing code creating marker you want to use the
573 601 `createmarkers` function in this module instead.
574 602
575 603 return True if a new marker have been added, False if the markers
576 604 already existed (no op).
577 605 """
578 606 if metadata is None:
579 607 metadata = {}
580 608 if date is None:
581 609 if 'date' in metadata:
582 610 # as a courtesy for out-of-tree extensions
583 611 date = util.parsedate(metadata.pop('date'))
584 612 elif ui is not None:
585 613 date = ui.configdate('devel', 'default-date')
586 614 if date is None:
587 615 date = util.makedate()
588 616 else:
589 617 date = util.makedate()
590 618 if len(prec) != 20:
591 619 raise ValueError(prec)
592 620 for succ in succs:
593 621 if len(succ) != 20:
594 622 raise ValueError(succ)
595 623 if prec in succs:
596 624 raise ValueError(_('in-marker cycle with %s') % node.hex(prec))
597 625
598 626 metadata = tuple(sorted(metadata.iteritems()))
599 627
600 628 marker = (bytes(prec), tuple(succs), int(flag), metadata, date, parents)
601 629 return bool(self.add(transaction, [marker]))
602 630
603 631 def add(self, transaction, markers):
604 632 """Add new markers to the store
605 633
606 634 Take care of filtering duplicate.
607 635 Return the number of new marker."""
608 636 if self._readonly:
609 637 raise error.Abort(_('creating obsolete markers is not enabled on '
610 638 'this repo'))
611 639 known = set()
612 640 getsuccessors = self.successors.get
613 641 new = []
614 642 for m in markers:
615 643 if m not in getsuccessors(m[0], ()) and m not in known:
616 644 known.add(m)
617 645 new.append(m)
618 646 if new:
619 647 f = self.svfs('obsstore', 'ab')
620 648 try:
621 649 offset = f.tell()
622 650 transaction.add('obsstore', offset)
623 651 # offset == 0: new file - add the version header
624 652 data = b''.join(encodemarkers(new, offset == 0, self._version))
625 653 f.write(data)
626 654 finally:
627 655 # XXX: f.close() == filecache invalidation == obsstore rebuilt.
628 656 # call 'filecacheentry.refresh()' here
629 657 f.close()
630 658 addedmarkers = transaction.changes.get('obsmarkers')
631 659 if addedmarkers is not None:
632 660 addedmarkers.update(new)
633 661 self._addmarkers(new, data)
634 662 # new marker *may* have changed several set. invalidate the cache.
635 663 self.caches.clear()
636 664 # records the number of new markers for the transaction hooks
637 665 previous = int(transaction.hookargs.get('new_obsmarkers', '0'))
638 666 transaction.hookargs['new_obsmarkers'] = str(previous + len(new))
639 667 return len(new)
640 668
641 669 def mergemarkers(self, transaction, data):
642 670 """merge a binary stream of markers inside the obsstore
643 671
644 672 Returns the number of new markers added."""
645 673 version, markers = _readmarkers(data)
646 674 return self.add(transaction, markers)
647 675
648 676 @propertycache
649 677 def _data(self):
650 678 return self.svfs.tryread('obsstore')
651 679
652 680 @propertycache
653 681 def _version(self):
654 682 if len(self._data) >= 1:
655 683 return _readmarkerversion(self._data)
656 684 else:
657 685 return self._defaultformat
658 686
659 687 @propertycache
660 688 def _all(self):
661 689 data = self._data
662 690 if not data:
663 691 return []
664 692 self._version, markers = _readmarkers(data)
665 693 markers = list(markers)
666 694 _checkinvalidmarkers(markers)
667 695 return markers
668 696
669 697 @propertycache
670 698 def successors(self):
671 699 successors = {}
672 700 _addsuccessors(successors, self._all)
673 701 return successors
674 702
675 703 @property
676 704 def precursors(self):
677 705 msg = ("'obsstore.precursors' is deprecated, "
678 706 "use 'obsstore.predecessors'")
679 707 util.nouideprecwarn(msg, '4.4')
680 708
681 709 return self.predecessors
682 710
683 711 @propertycache
684 712 def predecessors(self):
685 713 predecessors = {}
686 714 _addpredecessors(predecessors, self._all)
687 715 return predecessors
688 716
689 717 @propertycache
690 718 def children(self):
691 719 children = {}
692 720 _addchildren(children, self._all)
693 721 return children
694 722
695 723 def _cached(self, attr):
696 724 return attr in self.__dict__
697 725
698 726 def _addmarkers(self, markers, rawdata):
699 727 markers = list(markers) # to allow repeated iteration
700 728 self._data = self._data + rawdata
701 729 self._all.extend(markers)
702 730 if self._cached('successors'):
703 731 _addsuccessors(self.successors, markers)
704 732 if self._cached('predecessors'):
705 733 _addpredecessors(self.predecessors, markers)
706 734 if self._cached('children'):
707 735 _addchildren(self.children, markers)
708 736 _checkinvalidmarkers(markers)
709 737
710 738 def relevantmarkers(self, nodes):
711 739 """return a set of all obsolescence markers relevant to a set of nodes.
712 740
713 741 "relevant" to a set of nodes mean:
714 742
715 743 - marker that use this changeset as successor
716 744 - prune marker of direct children on this changeset
717 745 - recursive application of the two rules on predecessors of these
718 746 markers
719 747
720 748 It is a set so you cannot rely on order."""
721 749
722 750 pendingnodes = set(nodes)
723 751 seenmarkers = set()
724 752 seennodes = set(pendingnodes)
725 753 precursorsmarkers = self.predecessors
726 754 succsmarkers = self.successors
727 755 children = self.children
728 756 while pendingnodes:
729 757 direct = set()
730 758 for current in pendingnodes:
731 759 direct.update(precursorsmarkers.get(current, ()))
732 760 pruned = [m for m in children.get(current, ()) if not m[1]]
733 761 direct.update(pruned)
734 762 pruned = [m for m in succsmarkers.get(current, ()) if not m[1]]
735 763 direct.update(pruned)
736 764 direct -= seenmarkers
737 765 pendingnodes = set([m[0] for m in direct])
738 766 seenmarkers |= direct
739 767 pendingnodes -= seennodes
740 768 seennodes |= pendingnodes
741 769 return seenmarkers
742 770
743 771 def makestore(ui, repo):
744 772 """Create an obsstore instance from a repo."""
745 773 # read default format for new obsstore.
746 774 # developer config: format.obsstore-version
747 775 defaultformat = ui.configint('format', 'obsstore-version')
748 776 # rely on obsstore class default when possible.
749 777 kwargs = {}
750 778 if defaultformat is not None:
751 779 kwargs['defaultformat'] = defaultformat
752 780 readonly = not isenabled(repo, createmarkersopt)
753 781 store = obsstore(repo.svfs, readonly=readonly, **kwargs)
754 782 if store and readonly:
755 783 ui.warn(_('obsolete feature not enabled but %i markers found!\n')
756 784 % len(list(store)))
757 785 return store
758 786
759 787 def commonversion(versions):
760 788 """Return the newest version listed in both versions and our local formats.
761 789
762 790 Returns None if no common version exists.
763 791 """
764 792 versions.sort(reverse=True)
765 793 # search for highest version known on both side
766 794 for v in versions:
767 795 if v in formats:
768 796 return v
769 797 return None
770 798
771 799 # arbitrary picked to fit into 8K limit from HTTP server
772 800 # you have to take in account:
773 801 # - the version header
774 802 # - the base85 encoding
775 803 _maxpayload = 5300
776 804
777 805 def _pushkeyescape(markers):
778 806 """encode markers into a dict suitable for pushkey exchange
779 807
780 808 - binary data is base85 encoded
781 809 - split in chunks smaller than 5300 bytes"""
782 810 keys = {}
783 811 parts = []
784 812 currentlen = _maxpayload * 2 # ensure we create a new part
785 813 for marker in markers:
786 814 nextdata = _fm0encodeonemarker(marker)
787 815 if (len(nextdata) + currentlen > _maxpayload):
788 816 currentpart = []
789 817 currentlen = 0
790 818 parts.append(currentpart)
791 819 currentpart.append(nextdata)
792 820 currentlen += len(nextdata)
793 821 for idx, part in enumerate(reversed(parts)):
794 822 data = ''.join([_pack('>B', _fm0version)] + part)
795 823 keys['dump%i' % idx] = util.b85encode(data)
796 824 return keys
797 825
798 826 def listmarkers(repo):
799 827 """List markers over pushkey"""
800 828 if not repo.obsstore:
801 829 return {}
802 830 return _pushkeyescape(sorted(repo.obsstore))
803 831
804 832 def pushmarker(repo, key, old, new):
805 833 """Push markers over pushkey"""
806 834 if not key.startswith('dump'):
807 835 repo.ui.warn(_('unknown key: %r') % key)
808 836 return False
809 837 if old:
810 838 repo.ui.warn(_('unexpected old value for %r') % key)
811 839 return False
812 840 data = util.b85decode(new)
813 841 lock = repo.lock()
814 842 try:
815 843 tr = repo.transaction('pushkey: obsolete markers')
816 844 try:
817 845 repo.obsstore.mergemarkers(tr, data)
818 846 repo.invalidatevolatilesets()
819 847 tr.close()
820 848 return True
821 849 finally:
822 850 tr.release()
823 851 finally:
824 852 lock.release()
825 853
826 854 # keep compatibility for the 4.3 cycle
827 855 def allprecursors(obsstore, nodes, ignoreflags=0):
828 856 movemsg = 'obsolete.allprecursors moved to obsutil.allprecursors'
829 857 util.nouideprecwarn(movemsg, '4.3')
830 858 return obsutil.allprecursors(obsstore, nodes, ignoreflags)
831 859
832 860 def allsuccessors(obsstore, nodes, ignoreflags=0):
833 861 movemsg = 'obsolete.allsuccessors moved to obsutil.allsuccessors'
834 862 util.nouideprecwarn(movemsg, '4.3')
835 863 return obsutil.allsuccessors(obsstore, nodes, ignoreflags)
836 864
837 865 def marker(repo, data):
838 866 movemsg = 'obsolete.marker moved to obsutil.marker'
839 867 repo.ui.deprecwarn(movemsg, '4.3')
840 868 return obsutil.marker(repo, data)
841 869
842 870 def getmarkers(repo, nodes=None, exclusive=False):
843 871 movemsg = 'obsolete.getmarkers moved to obsutil.getmarkers'
844 872 repo.ui.deprecwarn(movemsg, '4.3')
845 873 return obsutil.getmarkers(repo, nodes=nodes, exclusive=exclusive)
846 874
847 875 def exclusivemarkers(repo, nodes):
848 876 movemsg = 'obsolete.exclusivemarkers moved to obsutil.exclusivemarkers'
849 877 repo.ui.deprecwarn(movemsg, '4.3')
850 878 return obsutil.exclusivemarkers(repo, nodes)
851 879
852 880 def foreground(repo, nodes):
853 881 movemsg = 'obsolete.foreground moved to obsutil.foreground'
854 882 repo.ui.deprecwarn(movemsg, '4.3')
855 883 return obsutil.foreground(repo, nodes)
856 884
857 885 def successorssets(repo, initialnode, cache=None):
858 886 movemsg = 'obsolete.successorssets moved to obsutil.successorssets'
859 887 repo.ui.deprecwarn(movemsg, '4.3')
860 888 return obsutil.successorssets(repo, initialnode, cache=cache)
861 889
862 890 # mapping of 'set-name' -> <function to compute this set>
863 891 cachefuncs = {}
864 892 def cachefor(name):
865 893 """Decorator to register a function as computing the cache for a set"""
866 894 def decorator(func):
867 895 if name in cachefuncs:
868 896 msg = "duplicated registration for volatileset '%s' (existing: %r)"
869 897 raise error.ProgrammingError(msg % (name, cachefuncs[name]))
870 898 cachefuncs[name] = func
871 899 return func
872 900 return decorator
873 901
874 902 def getrevs(repo, name):
875 903 """Return the set of revision that belong to the <name> set
876 904
877 905 Such access may compute the set and cache it for future use"""
878 906 repo = repo.unfiltered()
879 907 if not repo.obsstore:
880 908 return frozenset()
881 909 if name not in repo.obsstore.caches:
882 910 repo.obsstore.caches[name] = cachefuncs[name](repo)
883 911 return repo.obsstore.caches[name]
884 912
885 913 # To be simple we need to invalidate obsolescence cache when:
886 914 #
887 915 # - new changeset is added:
888 916 # - public phase is changed
889 917 # - obsolescence marker are added
890 918 # - strip is used a repo
891 919 def clearobscaches(repo):
892 920 """Remove all obsolescence related cache from a repo
893 921
894 922 This remove all cache in obsstore is the obsstore already exist on the
895 923 repo.
896 924
897 925 (We could be smarter here given the exact event that trigger the cache
898 926 clearing)"""
899 927 # only clear cache is there is obsstore data in this repo
900 928 if 'obsstore' in repo._filecache:
901 929 repo.obsstore.caches.clear()
902 930
903 931 def _mutablerevs(repo):
904 932 """the set of mutable revision in the repository"""
905 933 return repo._phasecache.getrevset(repo, (phases.draft, phases.secret))
906 934
907 935 @cachefor('obsolete')
908 936 def _computeobsoleteset(repo):
909 937 """the set of obsolete revisions"""
910 938 getnode = repo.changelog.node
911 939 notpublic = _mutablerevs(repo)
912 940 isobs = repo.obsstore.successors.__contains__
913 941 obs = set(r for r in notpublic if isobs(getnode(r)))
914 942 return obs
915 943
916 944 @cachefor('unstable')
917 945 def _computeunstableset(repo):
918 946 msg = ("'unstable' volatile set is deprecated, "
919 947 "use 'orphan'")
920 948 repo.ui.deprecwarn(msg, '4.4')
921 949
922 950 return _computeorphanset(repo)
923 951
924 952 @cachefor('orphan')
925 953 def _computeorphanset(repo):
926 954 """the set of non obsolete revisions with obsolete parents"""
927 955 pfunc = repo.changelog.parentrevs
928 956 mutable = _mutablerevs(repo)
929 957 obsolete = getrevs(repo, 'obsolete')
930 958 others = mutable - obsolete
931 959 unstable = set()
932 960 for r in sorted(others):
933 961 # A rev is unstable if one of its parent is obsolete or unstable
934 962 # this works since we traverse following growing rev order
935 963 for p in pfunc(r):
936 964 if p in obsolete or p in unstable:
937 965 unstable.add(r)
938 966 break
939 967 return unstable
940 968
941 969 @cachefor('suspended')
942 970 def _computesuspendedset(repo):
943 971 """the set of obsolete parents with non obsolete descendants"""
944 972 suspended = repo.changelog.ancestors(getrevs(repo, 'orphan'))
945 973 return set(r for r in getrevs(repo, 'obsolete') if r in suspended)
946 974
947 975 @cachefor('extinct')
948 976 def _computeextinctset(repo):
949 977 """the set of obsolete parents without non obsolete descendants"""
950 978 return getrevs(repo, 'obsolete') - getrevs(repo, 'suspended')
951 979
952 980 @cachefor('bumped')
953 981 def _computebumpedset(repo):
954 982 msg = ("'bumped' volatile set is deprecated, "
955 983 "use 'phasedivergent'")
956 984 repo.ui.deprecwarn(msg, '4.4')
957 985
958 986 return _computephasedivergentset(repo)
959 987
960 988 @cachefor('phasedivergent')
961 989 def _computephasedivergentset(repo):
962 990 """the set of revs trying to obsolete public revisions"""
963 991 bumped = set()
964 992 # util function (avoid attribute lookup in the loop)
965 993 phase = repo._phasecache.phase # would be faster to grab the full list
966 994 public = phases.public
967 995 cl = repo.changelog
968 996 torev = cl.nodemap.get
969 997 for ctx in repo.set('(not public()) and (not obsolete())'):
970 998 rev = ctx.rev()
971 999 # We only evaluate mutable, non-obsolete revision
972 1000 node = ctx.node()
973 1001 # (future) A cache of predecessors may worth if split is very common
974 1002 for pnode in obsutil.allpredecessors(repo.obsstore, [node],
975 1003 ignoreflags=bumpedfix):
976 1004 prev = torev(pnode) # unfiltered! but so is phasecache
977 1005 if (prev is not None) and (phase(repo, prev) <= public):
978 1006 # we have a public predecessor
979 1007 bumped.add(rev)
980 1008 break # Next draft!
981 1009 return bumped
982 1010
983 1011 @cachefor('divergent')
984 1012 def _computedivergentset(repo):
985 1013 msg = ("'divergent' volatile set is deprecated, "
986 1014 "use 'contentdivergent'")
987 1015 repo.ui.deprecwarn(msg, '4.4')
988 1016
989 1017 return _computecontentdivergentset(repo)
990 1018
991 1019 @cachefor('contentdivergent')
992 1020 def _computecontentdivergentset(repo):
993 1021 """the set of rev that compete to be the final successors of some revision.
994 1022 """
995 1023 divergent = set()
996 1024 obsstore = repo.obsstore
997 1025 newermap = {}
998 1026 for ctx in repo.set('(not public()) - obsolete()'):
999 1027 mark = obsstore.predecessors.get(ctx.node(), ())
1000 1028 toprocess = set(mark)
1001 1029 seen = set()
1002 1030 while toprocess:
1003 1031 prec = toprocess.pop()[0]
1004 1032 if prec in seen:
1005 1033 continue # emergency cycle hanging prevention
1006 1034 seen.add(prec)
1007 1035 if prec not in newermap:
1008 1036 obsutil.successorssets(repo, prec, cache=newermap)
1009 1037 newer = [n for n in newermap[prec] if n]
1010 1038 if len(newer) > 1:
1011 1039 divergent.add(ctx.rev())
1012 1040 break
1013 1041 toprocess.update(obsstore.predecessors.get(prec, ()))
1014 1042 return divergent
1015 1043
1016 1044
1017 1045 def createmarkers(repo, relations, flag=0, date=None, metadata=None,
1018 1046 operation=None):
1019 1047 """Add obsolete markers between changesets in a repo
1020 1048
1021 1049 <relations> must be an iterable of (<old>, (<new>, ...)[,{metadata}])
1022 1050 tuple. `old` and `news` are changectx. metadata is an optional dictionary
1023 1051 containing metadata for this marker only. It is merged with the global
1024 1052 metadata specified through the `metadata` argument of this function,
1025 1053
1026 1054 Trying to obsolete a public changeset will raise an exception.
1027 1055
1028 1056 Current user and date are used except if specified otherwise in the
1029 1057 metadata attribute.
1030 1058
1031 1059 This function operates within a transaction of its own, but does
1032 1060 not take any lock on the repo.
1033 1061 """
1034 1062 # prepare metadata
1035 1063 if metadata is None:
1036 1064 metadata = {}
1037 1065 if 'user' not in metadata:
1038 1066 develuser = repo.ui.config('devel', 'user.obsmarker')
1039 1067 if develuser:
1040 1068 metadata['user'] = develuser
1041 1069 else:
1042 1070 metadata['user'] = repo.ui.username()
1043 1071
1044 1072 # Operation metadata handling
1045 1073 useoperation = repo.ui.configbool('experimental',
1046 1074 'evolution.track-operation')
1047 1075 if useoperation and operation:
1048 1076 metadata['operation'] = operation
1049 1077
1050 1078 # Effect flag metadata handling
1051 1079 saveeffectflag = repo.ui.configbool('experimental',
1052 1080 'effect-flags')
1053 1081
1054 1082 tr = repo.transaction('add-obsolescence-marker')
1055 1083 try:
1056 1084 markerargs = []
1057 1085 for rel in relations:
1058 1086 prec = rel[0]
1059 1087 sucs = rel[1]
1060 1088 localmetadata = metadata.copy()
1061 1089 if 2 < len(rel):
1062 1090 localmetadata.update(rel[2])
1063 1091
1064 1092 if not prec.mutable():
1065 1093 raise error.Abort(_("cannot obsolete public changeset: %s")
1066 1094 % prec,
1067 1095 hint="see 'hg help phases' for details")
1068 1096 nprec = prec.node()
1069 1097 nsucs = tuple(s.node() for s in sucs)
1070 1098 npare = None
1071 1099 if not nsucs:
1072 1100 npare = tuple(p.node() for p in prec.parents())
1073 1101 if nprec in nsucs:
1074 1102 raise error.Abort(_("changeset %s cannot obsolete itself")
1075 1103 % prec)
1076 1104
1077 1105 # Effect flag can be different by relation
1078 1106 if saveeffectflag:
1079 1107 # The effect flag is saved in a versioned field name for future
1080 1108 # evolution
1081 1109 effectflag = obsutil.geteffectflag(rel)
1082 1110 localmetadata[obsutil.EFFECTFLAGFIELD] = "%d" % effectflag
1083 1111
1084 1112 # Creating the marker causes the hidden cache to become invalid,
1085 1113 # which causes recomputation when we ask for prec.parents() above.
1086 1114 # Resulting in n^2 behavior. So let's prepare all of the args
1087 1115 # first, then create the markers.
1088 1116 markerargs.append((nprec, nsucs, npare, localmetadata))
1089 1117
1090 1118 for args in markerargs:
1091 1119 nprec, nsucs, npare, localmetadata = args
1092 1120 repo.obsstore.create(tr, nprec, nsucs, flag, parents=npare,
1093 1121 date=date, metadata=localmetadata,
1094 1122 ui=repo.ui)
1095 1123 repo.filteredrevcache.clear()
1096 1124 tr.close()
1097 1125 finally:
1098 1126 tr.release()
General Comments 0
You need to be logged in to leave comments. Login now