##// END OF EJS Templates
copies: do copy tracing based on ctx.p[12]copies() if configured...
Martin von Zweigbergk -
r41922:49ad315b default
parent child Browse files
Show More
@@ -1,1449 +1,1452 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 def _registerdiffopts(section, configprefix=''):
117 117 coreconfigitem(section, configprefix + 'nodates',
118 118 default=False,
119 119 )
120 120 coreconfigitem(section, configprefix + 'showfunc',
121 121 default=False,
122 122 )
123 123 coreconfigitem(section, configprefix + 'unified',
124 124 default=None,
125 125 )
126 126 coreconfigitem(section, configprefix + 'git',
127 127 default=False,
128 128 )
129 129 coreconfigitem(section, configprefix + 'ignorews',
130 130 default=False,
131 131 )
132 132 coreconfigitem(section, configprefix + 'ignorewsamount',
133 133 default=False,
134 134 )
135 135 coreconfigitem(section, configprefix + 'ignoreblanklines',
136 136 default=False,
137 137 )
138 138 coreconfigitem(section, configprefix + 'ignorewseol',
139 139 default=False,
140 140 )
141 141 coreconfigitem(section, configprefix + 'nobinary',
142 142 default=False,
143 143 )
144 144 coreconfigitem(section, configprefix + 'noprefix',
145 145 default=False,
146 146 )
147 147 coreconfigitem(section, configprefix + 'word-diff',
148 148 default=False,
149 149 )
150 150
151 151 coreconfigitem('alias', '.*',
152 152 default=dynamicdefault,
153 153 generic=True,
154 154 )
155 155 coreconfigitem('auth', 'cookiefile',
156 156 default=None,
157 157 )
158 158 _registerdiffopts(section='annotate')
159 159 # bookmarks.pushing: internal hack for discovery
160 160 coreconfigitem('bookmarks', 'pushing',
161 161 default=list,
162 162 )
163 163 # bundle.mainreporoot: internal hack for bundlerepo
164 164 coreconfigitem('bundle', 'mainreporoot',
165 165 default='',
166 166 )
167 167 coreconfigitem('censor', 'policy',
168 168 default='abort',
169 169 )
170 170 coreconfigitem('chgserver', 'idletimeout',
171 171 default=3600,
172 172 )
173 173 coreconfigitem('chgserver', 'skiphash',
174 174 default=False,
175 175 )
176 176 coreconfigitem('cmdserver', 'log',
177 177 default=None,
178 178 )
179 179 coreconfigitem('cmdserver', 'max-log-files',
180 180 default=7,
181 181 )
182 182 coreconfigitem('cmdserver', 'max-log-size',
183 183 default='1 MB',
184 184 )
185 185 coreconfigitem('cmdserver', 'max-repo-cache',
186 186 default=0,
187 187 )
188 188 coreconfigitem('cmdserver', 'message-encodings',
189 189 default=list,
190 190 )
191 191 coreconfigitem('cmdserver', 'track-log',
192 192 default=lambda: ['chgserver', 'cmdserver', 'repocache'],
193 193 )
194 194 coreconfigitem('color', '.*',
195 195 default=None,
196 196 generic=True,
197 197 )
198 198 coreconfigitem('color', 'mode',
199 199 default='auto',
200 200 )
201 201 coreconfigitem('color', 'pagermode',
202 202 default=dynamicdefault,
203 203 )
204 204 _registerdiffopts(section='commands', configprefix='commit.interactive.')
205 205 coreconfigitem('commands', 'grep.all-files',
206 206 default=False,
207 207 )
208 208 coreconfigitem('commands', 'resolve.confirm',
209 209 default=False,
210 210 )
211 211 coreconfigitem('commands', 'resolve.explicit-re-merge',
212 212 default=False,
213 213 )
214 214 coreconfigitem('commands', 'resolve.mark-check',
215 215 default='none',
216 216 )
217 217 _registerdiffopts(section='commands', configprefix='revert.interactive.')
218 218 coreconfigitem('commands', 'show.aliasprefix',
219 219 default=list,
220 220 )
221 221 coreconfigitem('commands', 'status.relative',
222 222 default=False,
223 223 )
224 224 coreconfigitem('commands', 'status.skipstates',
225 225 default=[],
226 226 )
227 227 coreconfigitem('commands', 'status.terse',
228 228 default='',
229 229 )
230 230 coreconfigitem('commands', 'status.verbose',
231 231 default=False,
232 232 )
233 233 coreconfigitem('commands', 'update.check',
234 234 default=None,
235 235 )
236 236 coreconfigitem('commands', 'update.requiredest',
237 237 default=False,
238 238 )
239 239 coreconfigitem('committemplate', '.*',
240 240 default=None,
241 241 generic=True,
242 242 )
243 243 coreconfigitem('convert', 'bzr.saverev',
244 244 default=True,
245 245 )
246 246 coreconfigitem('convert', 'cvsps.cache',
247 247 default=True,
248 248 )
249 249 coreconfigitem('convert', 'cvsps.fuzz',
250 250 default=60,
251 251 )
252 252 coreconfigitem('convert', 'cvsps.logencoding',
253 253 default=None,
254 254 )
255 255 coreconfigitem('convert', 'cvsps.mergefrom',
256 256 default=None,
257 257 )
258 258 coreconfigitem('convert', 'cvsps.mergeto',
259 259 default=None,
260 260 )
261 261 coreconfigitem('convert', 'git.committeractions',
262 262 default=lambda: ['messagedifferent'],
263 263 )
264 264 coreconfigitem('convert', 'git.extrakeys',
265 265 default=list,
266 266 )
267 267 coreconfigitem('convert', 'git.findcopiesharder',
268 268 default=False,
269 269 )
270 270 coreconfigitem('convert', 'git.remoteprefix',
271 271 default='remote',
272 272 )
273 273 coreconfigitem('convert', 'git.renamelimit',
274 274 default=400,
275 275 )
276 276 coreconfigitem('convert', 'git.saverev',
277 277 default=True,
278 278 )
279 279 coreconfigitem('convert', 'git.similarity',
280 280 default=50,
281 281 )
282 282 coreconfigitem('convert', 'git.skipsubmodules',
283 283 default=False,
284 284 )
285 285 coreconfigitem('convert', 'hg.clonebranches',
286 286 default=False,
287 287 )
288 288 coreconfigitem('convert', 'hg.ignoreerrors',
289 289 default=False,
290 290 )
291 291 coreconfigitem('convert', 'hg.revs',
292 292 default=None,
293 293 )
294 294 coreconfigitem('convert', 'hg.saverev',
295 295 default=False,
296 296 )
297 297 coreconfigitem('convert', 'hg.sourcename',
298 298 default=None,
299 299 )
300 300 coreconfigitem('convert', 'hg.startrev',
301 301 default=None,
302 302 )
303 303 coreconfigitem('convert', 'hg.tagsbranch',
304 304 default='default',
305 305 )
306 306 coreconfigitem('convert', 'hg.usebranchnames',
307 307 default=True,
308 308 )
309 309 coreconfigitem('convert', 'ignoreancestorcheck',
310 310 default=False,
311 311 )
312 312 coreconfigitem('convert', 'localtimezone',
313 313 default=False,
314 314 )
315 315 coreconfigitem('convert', 'p4.encoding',
316 316 default=dynamicdefault,
317 317 )
318 318 coreconfigitem('convert', 'p4.startrev',
319 319 default=0,
320 320 )
321 321 coreconfigitem('convert', 'skiptags',
322 322 default=False,
323 323 )
324 324 coreconfigitem('convert', 'svn.debugsvnlog',
325 325 default=True,
326 326 )
327 327 coreconfigitem('convert', 'svn.trunk',
328 328 default=None,
329 329 )
330 330 coreconfigitem('convert', 'svn.tags',
331 331 default=None,
332 332 )
333 333 coreconfigitem('convert', 'svn.branches',
334 334 default=None,
335 335 )
336 336 coreconfigitem('convert', 'svn.startrev',
337 337 default=0,
338 338 )
339 339 coreconfigitem('debug', 'dirstate.delaywrite',
340 340 default=0,
341 341 )
342 342 coreconfigitem('defaults', '.*',
343 343 default=None,
344 344 generic=True,
345 345 )
346 346 coreconfigitem('devel', 'all-warnings',
347 347 default=False,
348 348 )
349 349 coreconfigitem('devel', 'bundle2.debug',
350 350 default=False,
351 351 )
352 352 coreconfigitem('devel', 'bundle.delta',
353 353 default='',
354 354 )
355 355 coreconfigitem('devel', 'cache-vfs',
356 356 default=None,
357 357 )
358 358 coreconfigitem('devel', 'check-locks',
359 359 default=False,
360 360 )
361 361 coreconfigitem('devel', 'check-relroot',
362 362 default=False,
363 363 )
364 364 coreconfigitem('devel', 'default-date',
365 365 default=None,
366 366 )
367 367 coreconfigitem('devel', 'deprec-warn',
368 368 default=False,
369 369 )
370 370 coreconfigitem('devel', 'disableloaddefaultcerts',
371 371 default=False,
372 372 )
373 373 coreconfigitem('devel', 'warn-empty-changegroup',
374 374 default=False,
375 375 )
376 376 coreconfigitem('devel', 'legacy.exchange',
377 377 default=list,
378 378 )
379 379 coreconfigitem('devel', 'servercafile',
380 380 default='',
381 381 )
382 382 coreconfigitem('devel', 'serverexactprotocol',
383 383 default='',
384 384 )
385 385 coreconfigitem('devel', 'serverrequirecert',
386 386 default=False,
387 387 )
388 388 coreconfigitem('devel', 'strip-obsmarkers',
389 389 default=True,
390 390 )
391 391 coreconfigitem('devel', 'warn-config',
392 392 default=None,
393 393 )
394 394 coreconfigitem('devel', 'warn-config-default',
395 395 default=None,
396 396 )
397 397 coreconfigitem('devel', 'user.obsmarker',
398 398 default=None,
399 399 )
400 400 coreconfigitem('devel', 'warn-config-unknown',
401 401 default=None,
402 402 )
403 403 coreconfigitem('devel', 'debug.copies',
404 404 default=False,
405 405 )
406 406 coreconfigitem('devel', 'debug.extensions',
407 407 default=False,
408 408 )
409 409 coreconfigitem('devel', 'debug.peer-request',
410 410 default=False,
411 411 )
412 412 _registerdiffopts(section='diff')
413 413 coreconfigitem('email', 'bcc',
414 414 default=None,
415 415 )
416 416 coreconfigitem('email', 'cc',
417 417 default=None,
418 418 )
419 419 coreconfigitem('email', 'charsets',
420 420 default=list,
421 421 )
422 422 coreconfigitem('email', 'from',
423 423 default=None,
424 424 )
425 425 coreconfigitem('email', 'method',
426 426 default='smtp',
427 427 )
428 428 coreconfigitem('email', 'reply-to',
429 429 default=None,
430 430 )
431 431 coreconfigitem('email', 'to',
432 432 default=None,
433 433 )
434 434 coreconfigitem('experimental', 'archivemetatemplate',
435 435 default=dynamicdefault,
436 436 )
437 437 coreconfigitem('experimental', 'auto-publish',
438 438 default='publish',
439 439 )
440 440 coreconfigitem('experimental', 'bundle-phases',
441 441 default=False,
442 442 )
443 443 coreconfigitem('experimental', 'bundle2-advertise',
444 444 default=True,
445 445 )
446 446 coreconfigitem('experimental', 'bundle2-output-capture',
447 447 default=False,
448 448 )
449 449 coreconfigitem('experimental', 'bundle2.pushback',
450 450 default=False,
451 451 )
452 452 coreconfigitem('experimental', 'bundle2lazylocking',
453 453 default=False,
454 454 )
455 455 coreconfigitem('experimental', 'bundlecomplevel',
456 456 default=None,
457 457 )
458 458 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
459 459 default=None,
460 460 )
461 461 coreconfigitem('experimental', 'bundlecomplevel.gzip',
462 462 default=None,
463 463 )
464 464 coreconfigitem('experimental', 'bundlecomplevel.none',
465 465 default=None,
466 466 )
467 467 coreconfigitem('experimental', 'bundlecomplevel.zstd',
468 468 default=None,
469 469 )
470 470 coreconfigitem('experimental', 'changegroup3',
471 471 default=False,
472 472 )
473 473 coreconfigitem('experimental', 'clientcompressionengines',
474 474 default=list,
475 475 )
476 476 coreconfigitem('experimental', 'copytrace',
477 477 default='on',
478 478 )
479 479 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
480 480 default=100,
481 481 )
482 482 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
483 483 default=100,
484 484 )
485 coreconfigitem('experimental', 'copies.read-from',
486 default="filelog-only",
487 )
485 488 coreconfigitem('experimental', 'crecordtest',
486 489 default=None,
487 490 )
488 491 coreconfigitem('experimental', 'directaccess',
489 492 default=False,
490 493 )
491 494 coreconfigitem('experimental', 'directaccess.revnums',
492 495 default=False,
493 496 )
494 497 coreconfigitem('experimental', 'editortmpinhg',
495 498 default=False,
496 499 )
497 500 coreconfigitem('experimental', 'evolution',
498 501 default=list,
499 502 )
500 503 coreconfigitem('experimental', 'evolution.allowdivergence',
501 504 default=False,
502 505 alias=[('experimental', 'allowdivergence')]
503 506 )
504 507 coreconfigitem('experimental', 'evolution.allowunstable',
505 508 default=None,
506 509 )
507 510 coreconfigitem('experimental', 'evolution.createmarkers',
508 511 default=None,
509 512 )
510 513 coreconfigitem('experimental', 'evolution.effect-flags',
511 514 default=True,
512 515 alias=[('experimental', 'effect-flags')]
513 516 )
514 517 coreconfigitem('experimental', 'evolution.exchange',
515 518 default=None,
516 519 )
517 520 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
518 521 default=False,
519 522 )
520 523 coreconfigitem('experimental', 'evolution.report-instabilities',
521 524 default=True,
522 525 )
523 526 coreconfigitem('experimental', 'evolution.track-operation',
524 527 default=True,
525 528 )
526 529 coreconfigitem('experimental', 'maxdeltachainspan',
527 530 default=-1,
528 531 )
529 532 coreconfigitem('experimental', 'mergetempdirprefix',
530 533 default=None,
531 534 )
532 535 coreconfigitem('experimental', 'mmapindexthreshold',
533 536 default=None,
534 537 )
535 538 coreconfigitem('experimental', 'narrow',
536 539 default=False,
537 540 )
538 541 coreconfigitem('experimental', 'nonnormalparanoidcheck',
539 542 default=False,
540 543 )
541 544 coreconfigitem('experimental', 'exportableenviron',
542 545 default=list,
543 546 )
544 547 coreconfigitem('experimental', 'extendedheader.index',
545 548 default=None,
546 549 )
547 550 coreconfigitem('experimental', 'extendedheader.similarity',
548 551 default=False,
549 552 )
550 553 coreconfigitem('experimental', 'format.compression',
551 554 default='zlib',
552 555 )
553 556 coreconfigitem('experimental', 'graphshorten',
554 557 default=False,
555 558 )
556 559 coreconfigitem('experimental', 'graphstyle.parent',
557 560 default=dynamicdefault,
558 561 )
559 562 coreconfigitem('experimental', 'graphstyle.missing',
560 563 default=dynamicdefault,
561 564 )
562 565 coreconfigitem('experimental', 'graphstyle.grandparent',
563 566 default=dynamicdefault,
564 567 )
565 568 coreconfigitem('experimental', 'hook-track-tags',
566 569 default=False,
567 570 )
568 571 coreconfigitem('experimental', 'httppeer.advertise-v2',
569 572 default=False,
570 573 )
571 574 coreconfigitem('experimental', 'httppeer.v2-encoder-order',
572 575 default=None,
573 576 )
574 577 coreconfigitem('experimental', 'httppostargs',
575 578 default=False,
576 579 )
577 580 coreconfigitem('experimental', 'mergedriver',
578 581 default=None,
579 582 )
580 583 coreconfigitem('experimental', 'nointerrupt', default=False)
581 584 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
582 585
583 586 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
584 587 default=False,
585 588 )
586 589 coreconfigitem('experimental', 'remotenames',
587 590 default=False,
588 591 )
589 592 coreconfigitem('experimental', 'removeemptydirs',
590 593 default=True,
591 594 )
592 595 coreconfigitem('experimental', 'revisions.prefixhexnode',
593 596 default=False,
594 597 )
595 598 coreconfigitem('experimental', 'revlogv2',
596 599 default=None,
597 600 )
598 601 coreconfigitem('experimental', 'revisions.disambiguatewithin',
599 602 default=None,
600 603 )
601 604 coreconfigitem('experimental', 'server.filesdata.recommended-batch-size',
602 605 default=50000,
603 606 )
604 607 coreconfigitem('experimental', 'server.manifestdata.recommended-batch-size',
605 608 default=100000,
606 609 )
607 610 coreconfigitem('experimental', 'server.stream-narrow-clones',
608 611 default=False,
609 612 )
610 613 coreconfigitem('experimental', 'single-head-per-branch',
611 614 default=False,
612 615 )
613 616 coreconfigitem('experimental', 'sshserver.support-v2',
614 617 default=False,
615 618 )
616 619 coreconfigitem('experimental', 'sparse-read',
617 620 default=False,
618 621 )
619 622 coreconfigitem('experimental', 'sparse-read.density-threshold',
620 623 default=0.50,
621 624 )
622 625 coreconfigitem('experimental', 'sparse-read.min-gap-size',
623 626 default='65K',
624 627 )
625 628 coreconfigitem('experimental', 'treemanifest',
626 629 default=False,
627 630 )
628 631 coreconfigitem('experimental', 'update.atomic-file',
629 632 default=False,
630 633 )
631 634 coreconfigitem('experimental', 'sshpeer.advertise-v2',
632 635 default=False,
633 636 )
634 637 coreconfigitem('experimental', 'web.apiserver',
635 638 default=False,
636 639 )
637 640 coreconfigitem('experimental', 'web.api.http-v2',
638 641 default=False,
639 642 )
640 643 coreconfigitem('experimental', 'web.api.debugreflect',
641 644 default=False,
642 645 )
643 646 coreconfigitem('experimental', 'worker.wdir-get-thread-safe',
644 647 default=False,
645 648 )
646 649 coreconfigitem('experimental', 'xdiff',
647 650 default=False,
648 651 )
649 652 coreconfigitem('extensions', '.*',
650 653 default=None,
651 654 generic=True,
652 655 )
653 656 coreconfigitem('extdata', '.*',
654 657 default=None,
655 658 generic=True,
656 659 )
657 660 coreconfigitem('format', 'chunkcachesize',
658 661 default=None,
659 662 )
660 663 coreconfigitem('format', 'dotencode',
661 664 default=True,
662 665 )
663 666 coreconfigitem('format', 'generaldelta',
664 667 default=False,
665 668 )
666 669 coreconfigitem('format', 'manifestcachesize',
667 670 default=None,
668 671 )
669 672 coreconfigitem('format', 'maxchainlen',
670 673 default=dynamicdefault,
671 674 )
672 675 coreconfigitem('format', 'obsstore-version',
673 676 default=None,
674 677 )
675 678 coreconfigitem('format', 'sparse-revlog',
676 679 default=True,
677 680 )
678 681 coreconfigitem('format', 'usefncache',
679 682 default=True,
680 683 )
681 684 coreconfigitem('format', 'usegeneraldelta',
682 685 default=True,
683 686 )
684 687 coreconfigitem('format', 'usestore',
685 688 default=True,
686 689 )
687 690 coreconfigitem('format', 'internal-phase',
688 691 default=False,
689 692 )
690 693 coreconfigitem('fsmonitor', 'warn_when_unused',
691 694 default=True,
692 695 )
693 696 coreconfigitem('fsmonitor', 'warn_update_file_count',
694 697 default=50000,
695 698 )
696 699 coreconfigitem('help', br'hidden-command\..*',
697 700 default=False,
698 701 generic=True,
699 702 )
700 703 coreconfigitem('help', br'hidden-topic\..*',
701 704 default=False,
702 705 generic=True,
703 706 )
704 707 coreconfigitem('hooks', '.*',
705 708 default=dynamicdefault,
706 709 generic=True,
707 710 )
708 711 coreconfigitem('hgweb-paths', '.*',
709 712 default=list,
710 713 generic=True,
711 714 )
712 715 coreconfigitem('hostfingerprints', '.*',
713 716 default=list,
714 717 generic=True,
715 718 )
716 719 coreconfigitem('hostsecurity', 'ciphers',
717 720 default=None,
718 721 )
719 722 coreconfigitem('hostsecurity', 'disabletls10warning',
720 723 default=False,
721 724 )
722 725 coreconfigitem('hostsecurity', 'minimumprotocol',
723 726 default=dynamicdefault,
724 727 )
725 728 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
726 729 default=dynamicdefault,
727 730 generic=True,
728 731 )
729 732 coreconfigitem('hostsecurity', '.*:ciphers$',
730 733 default=dynamicdefault,
731 734 generic=True,
732 735 )
733 736 coreconfigitem('hostsecurity', '.*:fingerprints$',
734 737 default=list,
735 738 generic=True,
736 739 )
737 740 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
738 741 default=None,
739 742 generic=True,
740 743 )
741 744
742 745 coreconfigitem('http_proxy', 'always',
743 746 default=False,
744 747 )
745 748 coreconfigitem('http_proxy', 'host',
746 749 default=None,
747 750 )
748 751 coreconfigitem('http_proxy', 'no',
749 752 default=list,
750 753 )
751 754 coreconfigitem('http_proxy', 'passwd',
752 755 default=None,
753 756 )
754 757 coreconfigitem('http_proxy', 'user',
755 758 default=None,
756 759 )
757 760
758 761 coreconfigitem('http', 'timeout',
759 762 default=None,
760 763 )
761 764
762 765 coreconfigitem('logtoprocess', 'commandexception',
763 766 default=None,
764 767 )
765 768 coreconfigitem('logtoprocess', 'commandfinish',
766 769 default=None,
767 770 )
768 771 coreconfigitem('logtoprocess', 'command',
769 772 default=None,
770 773 )
771 774 coreconfigitem('logtoprocess', 'develwarn',
772 775 default=None,
773 776 )
774 777 coreconfigitem('logtoprocess', 'uiblocked',
775 778 default=None,
776 779 )
777 780 coreconfigitem('merge', 'checkunknown',
778 781 default='abort',
779 782 )
780 783 coreconfigitem('merge', 'checkignored',
781 784 default='abort',
782 785 )
783 786 coreconfigitem('experimental', 'merge.checkpathconflicts',
784 787 default=False,
785 788 )
786 789 coreconfigitem('merge', 'followcopies',
787 790 default=True,
788 791 )
789 792 coreconfigitem('merge', 'on-failure',
790 793 default='continue',
791 794 )
792 795 coreconfigitem('merge', 'preferancestor',
793 796 default=lambda: ['*'],
794 797 )
795 798 coreconfigitem('merge', 'strict-capability-check',
796 799 default=False,
797 800 )
798 801 coreconfigitem('merge-tools', '.*',
799 802 default=None,
800 803 generic=True,
801 804 )
802 805 coreconfigitem('merge-tools', br'.*\.args$',
803 806 default="$local $base $other",
804 807 generic=True,
805 808 priority=-1,
806 809 )
807 810 coreconfigitem('merge-tools', br'.*\.binary$',
808 811 default=False,
809 812 generic=True,
810 813 priority=-1,
811 814 )
812 815 coreconfigitem('merge-tools', br'.*\.check$',
813 816 default=list,
814 817 generic=True,
815 818 priority=-1,
816 819 )
817 820 coreconfigitem('merge-tools', br'.*\.checkchanged$',
818 821 default=False,
819 822 generic=True,
820 823 priority=-1,
821 824 )
822 825 coreconfigitem('merge-tools', br'.*\.executable$',
823 826 default=dynamicdefault,
824 827 generic=True,
825 828 priority=-1,
826 829 )
827 830 coreconfigitem('merge-tools', br'.*\.fixeol$',
828 831 default=False,
829 832 generic=True,
830 833 priority=-1,
831 834 )
832 835 coreconfigitem('merge-tools', br'.*\.gui$',
833 836 default=False,
834 837 generic=True,
835 838 priority=-1,
836 839 )
837 840 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
838 841 default='basic',
839 842 generic=True,
840 843 priority=-1,
841 844 )
842 845 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
843 846 default=dynamicdefault, # take from ui.mergemarkertemplate
844 847 generic=True,
845 848 priority=-1,
846 849 )
847 850 coreconfigitem('merge-tools', br'.*\.priority$',
848 851 default=0,
849 852 generic=True,
850 853 priority=-1,
851 854 )
852 855 coreconfigitem('merge-tools', br'.*\.premerge$',
853 856 default=dynamicdefault,
854 857 generic=True,
855 858 priority=-1,
856 859 )
857 860 coreconfigitem('merge-tools', br'.*\.symlink$',
858 861 default=False,
859 862 generic=True,
860 863 priority=-1,
861 864 )
862 865 coreconfigitem('pager', 'attend-.*',
863 866 default=dynamicdefault,
864 867 generic=True,
865 868 )
866 869 coreconfigitem('pager', 'ignore',
867 870 default=list,
868 871 )
869 872 coreconfigitem('pager', 'pager',
870 873 default=dynamicdefault,
871 874 )
872 875 coreconfigitem('patch', 'eol',
873 876 default='strict',
874 877 )
875 878 coreconfigitem('patch', 'fuzz',
876 879 default=2,
877 880 )
878 881 coreconfigitem('paths', 'default',
879 882 default=None,
880 883 )
881 884 coreconfigitem('paths', 'default-push',
882 885 default=None,
883 886 )
884 887 coreconfigitem('paths', '.*',
885 888 default=None,
886 889 generic=True,
887 890 )
888 891 coreconfigitem('phases', 'checksubrepos',
889 892 default='follow',
890 893 )
891 894 coreconfigitem('phases', 'new-commit',
892 895 default='draft',
893 896 )
894 897 coreconfigitem('phases', 'publish',
895 898 default=True,
896 899 )
897 900 coreconfigitem('profiling', 'enabled',
898 901 default=False,
899 902 )
900 903 coreconfigitem('profiling', 'format',
901 904 default='text',
902 905 )
903 906 coreconfigitem('profiling', 'freq',
904 907 default=1000,
905 908 )
906 909 coreconfigitem('profiling', 'limit',
907 910 default=30,
908 911 )
909 912 coreconfigitem('profiling', 'nested',
910 913 default=0,
911 914 )
912 915 coreconfigitem('profiling', 'output',
913 916 default=None,
914 917 )
915 918 coreconfigitem('profiling', 'showmax',
916 919 default=0.999,
917 920 )
918 921 coreconfigitem('profiling', 'showmin',
919 922 default=dynamicdefault,
920 923 )
921 924 coreconfigitem('profiling', 'sort',
922 925 default='inlinetime',
923 926 )
924 927 coreconfigitem('profiling', 'statformat',
925 928 default='hotpath',
926 929 )
927 930 coreconfigitem('profiling', 'time-track',
928 931 default=dynamicdefault,
929 932 )
930 933 coreconfigitem('profiling', 'type',
931 934 default='stat',
932 935 )
933 936 coreconfigitem('progress', 'assume-tty',
934 937 default=False,
935 938 )
936 939 coreconfigitem('progress', 'changedelay',
937 940 default=1,
938 941 )
939 942 coreconfigitem('progress', 'clear-complete',
940 943 default=True,
941 944 )
942 945 coreconfigitem('progress', 'debug',
943 946 default=False,
944 947 )
945 948 coreconfigitem('progress', 'delay',
946 949 default=3,
947 950 )
948 951 coreconfigitem('progress', 'disable',
949 952 default=False,
950 953 )
951 954 coreconfigitem('progress', 'estimateinterval',
952 955 default=60.0,
953 956 )
954 957 coreconfigitem('progress', 'format',
955 958 default=lambda: ['topic', 'bar', 'number', 'estimate'],
956 959 )
957 960 coreconfigitem('progress', 'refresh',
958 961 default=0.1,
959 962 )
960 963 coreconfigitem('progress', 'width',
961 964 default=dynamicdefault,
962 965 )
963 966 coreconfigitem('push', 'pushvars.server',
964 967 default=False,
965 968 )
966 969 coreconfigitem('rewrite', 'backup-bundle',
967 970 default=True,
968 971 alias=[('ui', 'history-editing-backup')],
969 972 )
970 973 coreconfigitem('rewrite', 'update-timestamp',
971 974 default=False,
972 975 )
973 976 coreconfigitem('storage', 'new-repo-backend',
974 977 default='revlogv1',
975 978 )
976 979 coreconfigitem('storage', 'revlog.optimize-delta-parent-choice',
977 980 default=True,
978 981 alias=[('format', 'aggressivemergedeltas')],
979 982 )
980 983 coreconfigitem('server', 'bookmarks-pushkey-compat',
981 984 default=True,
982 985 )
983 986 coreconfigitem('server', 'bundle1',
984 987 default=True,
985 988 )
986 989 coreconfigitem('server', 'bundle1gd',
987 990 default=None,
988 991 )
989 992 coreconfigitem('server', 'bundle1.pull',
990 993 default=None,
991 994 )
992 995 coreconfigitem('server', 'bundle1gd.pull',
993 996 default=None,
994 997 )
995 998 coreconfigitem('server', 'bundle1.push',
996 999 default=None,
997 1000 )
998 1001 coreconfigitem('server', 'bundle1gd.push',
999 1002 default=None,
1000 1003 )
1001 1004 coreconfigitem('server', 'bundle2.stream',
1002 1005 default=True,
1003 1006 alias=[('experimental', 'bundle2.stream')]
1004 1007 )
1005 1008 coreconfigitem('server', 'compressionengines',
1006 1009 default=list,
1007 1010 )
1008 1011 coreconfigitem('server', 'concurrent-push-mode',
1009 1012 default='strict',
1010 1013 )
1011 1014 coreconfigitem('server', 'disablefullbundle',
1012 1015 default=False,
1013 1016 )
1014 1017 coreconfigitem('server', 'maxhttpheaderlen',
1015 1018 default=1024,
1016 1019 )
1017 1020 coreconfigitem('server', 'pullbundle',
1018 1021 default=False,
1019 1022 )
1020 1023 coreconfigitem('server', 'preferuncompressed',
1021 1024 default=False,
1022 1025 )
1023 1026 coreconfigitem('server', 'streamunbundle',
1024 1027 default=False,
1025 1028 )
1026 1029 coreconfigitem('server', 'uncompressed',
1027 1030 default=True,
1028 1031 )
1029 1032 coreconfigitem('server', 'uncompressedallowsecret',
1030 1033 default=False,
1031 1034 )
1032 1035 coreconfigitem('server', 'validate',
1033 1036 default=False,
1034 1037 )
1035 1038 coreconfigitem('server', 'zliblevel',
1036 1039 default=-1,
1037 1040 )
1038 1041 coreconfigitem('server', 'zstdlevel',
1039 1042 default=3,
1040 1043 )
1041 1044 coreconfigitem('share', 'pool',
1042 1045 default=None,
1043 1046 )
1044 1047 coreconfigitem('share', 'poolnaming',
1045 1048 default='identity',
1046 1049 )
1047 1050 coreconfigitem('smtp', 'host',
1048 1051 default=None,
1049 1052 )
1050 1053 coreconfigitem('smtp', 'local_hostname',
1051 1054 default=None,
1052 1055 )
1053 1056 coreconfigitem('smtp', 'password',
1054 1057 default=None,
1055 1058 )
1056 1059 coreconfigitem('smtp', 'port',
1057 1060 default=dynamicdefault,
1058 1061 )
1059 1062 coreconfigitem('smtp', 'tls',
1060 1063 default='none',
1061 1064 )
1062 1065 coreconfigitem('smtp', 'username',
1063 1066 default=None,
1064 1067 )
1065 1068 coreconfigitem('sparse', 'missingwarning',
1066 1069 default=True,
1067 1070 )
1068 1071 coreconfigitem('subrepos', 'allowed',
1069 1072 default=dynamicdefault, # to make backporting simpler
1070 1073 )
1071 1074 coreconfigitem('subrepos', 'hg:allowed',
1072 1075 default=dynamicdefault,
1073 1076 )
1074 1077 coreconfigitem('subrepos', 'git:allowed',
1075 1078 default=dynamicdefault,
1076 1079 )
1077 1080 coreconfigitem('subrepos', 'svn:allowed',
1078 1081 default=dynamicdefault,
1079 1082 )
1080 1083 coreconfigitem('templates', '.*',
1081 1084 default=None,
1082 1085 generic=True,
1083 1086 )
1084 1087 coreconfigitem('templateconfig', '.*',
1085 1088 default=dynamicdefault,
1086 1089 generic=True,
1087 1090 )
1088 1091 coreconfigitem('trusted', 'groups',
1089 1092 default=list,
1090 1093 )
1091 1094 coreconfigitem('trusted', 'users',
1092 1095 default=list,
1093 1096 )
1094 1097 coreconfigitem('ui', '_usedassubrepo',
1095 1098 default=False,
1096 1099 )
1097 1100 coreconfigitem('ui', 'allowemptycommit',
1098 1101 default=False,
1099 1102 )
1100 1103 coreconfigitem('ui', 'archivemeta',
1101 1104 default=True,
1102 1105 )
1103 1106 coreconfigitem('ui', 'askusername',
1104 1107 default=False,
1105 1108 )
1106 1109 coreconfigitem('ui', 'clonebundlefallback',
1107 1110 default=False,
1108 1111 )
1109 1112 coreconfigitem('ui', 'clonebundleprefers',
1110 1113 default=list,
1111 1114 )
1112 1115 coreconfigitem('ui', 'clonebundles',
1113 1116 default=True,
1114 1117 )
1115 1118 coreconfigitem('ui', 'color',
1116 1119 default='auto',
1117 1120 )
1118 1121 coreconfigitem('ui', 'commitsubrepos',
1119 1122 default=False,
1120 1123 )
1121 1124 coreconfigitem('ui', 'debug',
1122 1125 default=False,
1123 1126 )
1124 1127 coreconfigitem('ui', 'debugger',
1125 1128 default=None,
1126 1129 )
1127 1130 coreconfigitem('ui', 'editor',
1128 1131 default=dynamicdefault,
1129 1132 )
1130 1133 coreconfigitem('ui', 'fallbackencoding',
1131 1134 default=None,
1132 1135 )
1133 1136 coreconfigitem('ui', 'forcecwd',
1134 1137 default=None,
1135 1138 )
1136 1139 coreconfigitem('ui', 'forcemerge',
1137 1140 default=None,
1138 1141 )
1139 1142 coreconfigitem('ui', 'formatdebug',
1140 1143 default=False,
1141 1144 )
1142 1145 coreconfigitem('ui', 'formatjson',
1143 1146 default=False,
1144 1147 )
1145 1148 coreconfigitem('ui', 'formatted',
1146 1149 default=None,
1147 1150 )
1148 1151 coreconfigitem('ui', 'graphnodetemplate',
1149 1152 default=None,
1150 1153 )
1151 1154 coreconfigitem('ui', 'interactive',
1152 1155 default=None,
1153 1156 )
1154 1157 coreconfigitem('ui', 'interface',
1155 1158 default=None,
1156 1159 )
1157 1160 coreconfigitem('ui', 'interface.chunkselector',
1158 1161 default=None,
1159 1162 )
1160 1163 coreconfigitem('ui', 'large-file-limit',
1161 1164 default=10000000,
1162 1165 )
1163 1166 coreconfigitem('ui', 'logblockedtimes',
1164 1167 default=False,
1165 1168 )
1166 1169 coreconfigitem('ui', 'logtemplate',
1167 1170 default=None,
1168 1171 )
1169 1172 coreconfigitem('ui', 'merge',
1170 1173 default=None,
1171 1174 )
1172 1175 coreconfigitem('ui', 'mergemarkers',
1173 1176 default='basic',
1174 1177 )
1175 1178 coreconfigitem('ui', 'mergemarkertemplate',
1176 1179 default=('{node|short} '
1177 1180 '{ifeq(tags, "tip", "", '
1178 1181 'ifeq(tags, "", "", "{tags} "))}'
1179 1182 '{if(bookmarks, "{bookmarks} ")}'
1180 1183 '{ifeq(branch, "default", "", "{branch} ")}'
1181 1184 '- {author|user}: {desc|firstline}')
1182 1185 )
1183 1186 coreconfigitem('ui', 'message-output',
1184 1187 default='stdio',
1185 1188 )
1186 1189 coreconfigitem('ui', 'nontty',
1187 1190 default=False,
1188 1191 )
1189 1192 coreconfigitem('ui', 'origbackuppath',
1190 1193 default=None,
1191 1194 )
1192 1195 coreconfigitem('ui', 'paginate',
1193 1196 default=True,
1194 1197 )
1195 1198 coreconfigitem('ui', 'patch',
1196 1199 default=None,
1197 1200 )
1198 1201 coreconfigitem('ui', 'pre-merge-tool-output-template',
1199 1202 default=None,
1200 1203 )
1201 1204 coreconfigitem('ui', 'portablefilenames',
1202 1205 default='warn',
1203 1206 )
1204 1207 coreconfigitem('ui', 'promptecho',
1205 1208 default=False,
1206 1209 )
1207 1210 coreconfigitem('ui', 'quiet',
1208 1211 default=False,
1209 1212 )
1210 1213 coreconfigitem('ui', 'quietbookmarkmove',
1211 1214 default=False,
1212 1215 )
1213 1216 coreconfigitem('ui', 'relative-paths',
1214 1217 default='legacy',
1215 1218 )
1216 1219 coreconfigitem('ui', 'remotecmd',
1217 1220 default='hg',
1218 1221 )
1219 1222 coreconfigitem('ui', 'report_untrusted',
1220 1223 default=True,
1221 1224 )
1222 1225 coreconfigitem('ui', 'rollback',
1223 1226 default=True,
1224 1227 )
1225 1228 coreconfigitem('ui', 'signal-safe-lock',
1226 1229 default=True,
1227 1230 )
1228 1231 coreconfigitem('ui', 'slash',
1229 1232 default=False,
1230 1233 )
1231 1234 coreconfigitem('ui', 'ssh',
1232 1235 default='ssh',
1233 1236 )
1234 1237 coreconfigitem('ui', 'ssherrorhint',
1235 1238 default=None,
1236 1239 )
1237 1240 coreconfigitem('ui', 'statuscopies',
1238 1241 default=False,
1239 1242 )
1240 1243 coreconfigitem('ui', 'strict',
1241 1244 default=False,
1242 1245 )
1243 1246 coreconfigitem('ui', 'style',
1244 1247 default='',
1245 1248 )
1246 1249 coreconfigitem('ui', 'supportcontact',
1247 1250 default=None,
1248 1251 )
1249 1252 coreconfigitem('ui', 'textwidth',
1250 1253 default=78,
1251 1254 )
1252 1255 coreconfigitem('ui', 'timeout',
1253 1256 default='600',
1254 1257 )
1255 1258 coreconfigitem('ui', 'timeout.warn',
1256 1259 default=0,
1257 1260 )
1258 1261 coreconfigitem('ui', 'traceback',
1259 1262 default=False,
1260 1263 )
1261 1264 coreconfigitem('ui', 'tweakdefaults',
1262 1265 default=False,
1263 1266 )
1264 1267 coreconfigitem('ui', 'username',
1265 1268 alias=[('ui', 'user')]
1266 1269 )
1267 1270 coreconfigitem('ui', 'verbose',
1268 1271 default=False,
1269 1272 )
1270 1273 coreconfigitem('verify', 'skipflags',
1271 1274 default=None,
1272 1275 )
1273 1276 coreconfigitem('web', 'allowbz2',
1274 1277 default=False,
1275 1278 )
1276 1279 coreconfigitem('web', 'allowgz',
1277 1280 default=False,
1278 1281 )
1279 1282 coreconfigitem('web', 'allow-pull',
1280 1283 alias=[('web', 'allowpull')],
1281 1284 default=True,
1282 1285 )
1283 1286 coreconfigitem('web', 'allow-push',
1284 1287 alias=[('web', 'allow_push')],
1285 1288 default=list,
1286 1289 )
1287 1290 coreconfigitem('web', 'allowzip',
1288 1291 default=False,
1289 1292 )
1290 1293 coreconfigitem('web', 'archivesubrepos',
1291 1294 default=False,
1292 1295 )
1293 1296 coreconfigitem('web', 'cache',
1294 1297 default=True,
1295 1298 )
1296 1299 coreconfigitem('web', 'comparisoncontext',
1297 1300 default=5,
1298 1301 )
1299 1302 coreconfigitem('web', 'contact',
1300 1303 default=None,
1301 1304 )
1302 1305 coreconfigitem('web', 'deny_push',
1303 1306 default=list,
1304 1307 )
1305 1308 coreconfigitem('web', 'guessmime',
1306 1309 default=False,
1307 1310 )
1308 1311 coreconfigitem('web', 'hidden',
1309 1312 default=False,
1310 1313 )
1311 1314 coreconfigitem('web', 'labels',
1312 1315 default=list,
1313 1316 )
1314 1317 coreconfigitem('web', 'logoimg',
1315 1318 default='hglogo.png',
1316 1319 )
1317 1320 coreconfigitem('web', 'logourl',
1318 1321 default='https://mercurial-scm.org/',
1319 1322 )
1320 1323 coreconfigitem('web', 'accesslog',
1321 1324 default='-',
1322 1325 )
1323 1326 coreconfigitem('web', 'address',
1324 1327 default='',
1325 1328 )
1326 1329 coreconfigitem('web', 'allow-archive',
1327 1330 alias=[('web', 'allow_archive')],
1328 1331 default=list,
1329 1332 )
1330 1333 coreconfigitem('web', 'allow_read',
1331 1334 default=list,
1332 1335 )
1333 1336 coreconfigitem('web', 'baseurl',
1334 1337 default=None,
1335 1338 )
1336 1339 coreconfigitem('web', 'cacerts',
1337 1340 default=None,
1338 1341 )
1339 1342 coreconfigitem('web', 'certificate',
1340 1343 default=None,
1341 1344 )
1342 1345 coreconfigitem('web', 'collapse',
1343 1346 default=False,
1344 1347 )
1345 1348 coreconfigitem('web', 'csp',
1346 1349 default=None,
1347 1350 )
1348 1351 coreconfigitem('web', 'deny_read',
1349 1352 default=list,
1350 1353 )
1351 1354 coreconfigitem('web', 'descend',
1352 1355 default=True,
1353 1356 )
1354 1357 coreconfigitem('web', 'description',
1355 1358 default="",
1356 1359 )
1357 1360 coreconfigitem('web', 'encoding',
1358 1361 default=lambda: encoding.encoding,
1359 1362 )
1360 1363 coreconfigitem('web', 'errorlog',
1361 1364 default='-',
1362 1365 )
1363 1366 coreconfigitem('web', 'ipv6',
1364 1367 default=False,
1365 1368 )
1366 1369 coreconfigitem('web', 'maxchanges',
1367 1370 default=10,
1368 1371 )
1369 1372 coreconfigitem('web', 'maxfiles',
1370 1373 default=10,
1371 1374 )
1372 1375 coreconfigitem('web', 'maxshortchanges',
1373 1376 default=60,
1374 1377 )
1375 1378 coreconfigitem('web', 'motd',
1376 1379 default='',
1377 1380 )
1378 1381 coreconfigitem('web', 'name',
1379 1382 default=dynamicdefault,
1380 1383 )
1381 1384 coreconfigitem('web', 'port',
1382 1385 default=8000,
1383 1386 )
1384 1387 coreconfigitem('web', 'prefix',
1385 1388 default='',
1386 1389 )
1387 1390 coreconfigitem('web', 'push_ssl',
1388 1391 default=True,
1389 1392 )
1390 1393 coreconfigitem('web', 'refreshinterval',
1391 1394 default=20,
1392 1395 )
1393 1396 coreconfigitem('web', 'server-header',
1394 1397 default=None,
1395 1398 )
1396 1399 coreconfigitem('web', 'static',
1397 1400 default=None,
1398 1401 )
1399 1402 coreconfigitem('web', 'staticurl',
1400 1403 default=None,
1401 1404 )
1402 1405 coreconfigitem('web', 'stripes',
1403 1406 default=1,
1404 1407 )
1405 1408 coreconfigitem('web', 'style',
1406 1409 default='paper',
1407 1410 )
1408 1411 coreconfigitem('web', 'templates',
1409 1412 default=None,
1410 1413 )
1411 1414 coreconfigitem('web', 'view',
1412 1415 default='served',
1413 1416 )
1414 1417 coreconfigitem('worker', 'backgroundclose',
1415 1418 default=dynamicdefault,
1416 1419 )
1417 1420 # Windows defaults to a limit of 512 open files. A buffer of 128
1418 1421 # should give us enough headway.
1419 1422 coreconfigitem('worker', 'backgroundclosemaxqueue',
1420 1423 default=384,
1421 1424 )
1422 1425 coreconfigitem('worker', 'backgroundcloseminfilecount',
1423 1426 default=2048,
1424 1427 )
1425 1428 coreconfigitem('worker', 'backgroundclosethreadcount',
1426 1429 default=4,
1427 1430 )
1428 1431 coreconfigitem('worker', 'enabled',
1429 1432 default=True,
1430 1433 )
1431 1434 coreconfigitem('worker', 'numcpus',
1432 1435 default=None,
1433 1436 )
1434 1437
1435 1438 # Rebase related configuration moved to core because other extension are doing
1436 1439 # strange things. For example, shelve import the extensions to reuse some bit
1437 1440 # without formally loading it.
1438 1441 coreconfigitem('commands', 'rebase.requiredest',
1439 1442 default=False,
1440 1443 )
1441 1444 coreconfigitem('experimental', 'rebaseskipobsolete',
1442 1445 default=True,
1443 1446 )
1444 1447 coreconfigitem('rebase', 'singletransaction',
1445 1448 default=False,
1446 1449 )
1447 1450 coreconfigitem('rebase', 'experimental.inmemory',
1448 1451 default=False,
1449 1452 )
@@ -1,927 +1,1001 b''
1 1 # copies.py - copy detection for Mercurial
2 2 #
3 3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import collections
11 11 import heapq
12 12 import os
13 13
14 14 from .i18n import _
15 15
16 16 from . import (
17 17 match as matchmod,
18 18 node,
19 19 pathutil,
20 20 scmutil,
21 21 util,
22 22 )
23 23 from .utils import (
24 24 stringutil,
25 25 )
26 26
27 27 def _findlimit(repo, ctxa, ctxb):
28 28 """
29 29 Find the last revision that needs to be checked to ensure that a full
30 30 transitive closure for file copies can be properly calculated.
31 31 Generally, this means finding the earliest revision number that's an
32 32 ancestor of a or b but not both, except when a or b is a direct descendent
33 33 of the other, in which case we can return the minimum revnum of a and b.
34 34 """
35 35
36 36 # basic idea:
37 37 # - mark a and b with different sides
38 38 # - if a parent's children are all on the same side, the parent is
39 39 # on that side, otherwise it is on no side
40 40 # - walk the graph in topological order with the help of a heap;
41 41 # - add unseen parents to side map
42 42 # - clear side of any parent that has children on different sides
43 43 # - track number of interesting revs that might still be on a side
44 44 # - track the lowest interesting rev seen
45 45 # - quit when interesting revs is zero
46 46
47 47 cl = repo.changelog
48 48 wdirparents = None
49 49 a = ctxa.rev()
50 50 b = ctxb.rev()
51 51 if a is None:
52 52 wdirparents = (ctxa.p1(), ctxa.p2())
53 53 a = node.wdirrev
54 54 if b is None:
55 55 assert not wdirparents
56 56 wdirparents = (ctxb.p1(), ctxb.p2())
57 57 b = node.wdirrev
58 58
59 59 side = {a: -1, b: 1}
60 60 visit = [-a, -b]
61 61 heapq.heapify(visit)
62 62 interesting = len(visit)
63 63 limit = node.wdirrev
64 64
65 65 while interesting:
66 66 r = -heapq.heappop(visit)
67 67 if r == node.wdirrev:
68 68 parents = [pctx.rev() for pctx in wdirparents]
69 69 else:
70 70 parents = cl.parentrevs(r)
71 71 if parents[1] == node.nullrev:
72 72 parents = parents[:1]
73 73 for p in parents:
74 74 if p not in side:
75 75 # first time we see p; add it to visit
76 76 side[p] = side[r]
77 77 if side[p]:
78 78 interesting += 1
79 79 heapq.heappush(visit, -p)
80 80 elif side[p] and side[p] != side[r]:
81 81 # p was interesting but now we know better
82 82 side[p] = 0
83 83 interesting -= 1
84 84 if side[r]:
85 85 limit = r # lowest rev visited
86 86 interesting -= 1
87 87
88 88 # Consider the following flow (see test-commit-amend.t under issue4405):
89 89 # 1/ File 'a0' committed
90 90 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
91 91 # 3/ Move back to first commit
92 92 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
93 93 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
94 94 #
95 95 # During the amend in step five, we will be in this state:
96 96 #
97 97 # @ 3 temporary amend commit for a1-amend
98 98 # |
99 99 # o 2 a1-amend
100 100 # |
101 101 # | o 1 a1
102 102 # |/
103 103 # o 0 a0
104 104 #
105 105 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
106 106 # yet the filelog has the copy information in rev 1 and we will not look
107 107 # back far enough unless we also look at the a and b as candidates.
108 108 # This only occurs when a is a descendent of b or visa-versa.
109 109 return min(limit, a, b)
110 110
111 111 def _chain(src, dst, a, b):
112 112 """chain two sets of copies a->b"""
113 113 t = a.copy()
114 114 for k, v in b.iteritems():
115 115 if v in t:
116 116 # found a chain
117 117 if t[v] != k:
118 118 # file wasn't renamed back to itself
119 119 t[k] = t[v]
120 120 if v not in dst:
121 121 # chain was a rename, not a copy
122 122 del t[v]
123 123 if v in src:
124 124 # file is a copy of an existing file
125 125 t[k] = v
126 126
127 127 for k, v in list(t.items()):
128 128 # remove criss-crossed copies
129 129 if k in src and v in dst:
130 130 del t[k]
131 131 # remove copies to files that were then removed
132 132 elif k not in dst:
133 133 del t[k]
134 134
135 135 return t
136 136
137 137 def _tracefile(fctx, am, limit=node.nullrev):
138 138 """return file context that is the ancestor of fctx present in ancestor
139 139 manifest am, stopping after the first ancestor lower than limit"""
140 140
141 141 for f in fctx.ancestors():
142 142 if am.get(f.path(), None) == f.filenode():
143 143 return f
144 144 if limit >= 0 and not f.isintroducedafter(limit):
145 145 return None
146 146
147 147 def _dirstatecopies(repo, match=None):
148 148 ds = repo.dirstate
149 149 c = ds.copies().copy()
150 150 for k in list(c):
151 151 if ds[k] not in 'anm' or (match and not match(k)):
152 152 del c[k]
153 153 return c
154 154
155 155 def _computeforwardmissing(a, b, match=None):
156 156 """Computes which files are in b but not a.
157 157 This is its own function so extensions can easily wrap this call to see what
158 158 files _forwardcopies is about to process.
159 159 """
160 160 ma = a.manifest()
161 161 mb = b.manifest()
162 162 return mb.filesnotin(ma, match=match)
163 163
164 164 def _committedforwardcopies(a, b, match):
165 165 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
166 166 # files might have to be traced back to the fctx parent of the last
167 167 # one-side-only changeset, but not further back than that
168 168 repo = a._repo
169
170 if repo.ui.config('experimental', 'copies.read-from') == 'compatibility':
171 return _changesetforwardcopies(a, b, match)
172
169 173 debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies')
170 174 dbg = repo.ui.debug
171 175 if debug:
172 176 dbg('debug.copies: looking into rename from %s to %s\n'
173 177 % (a, b))
174 178 limit = _findlimit(repo, a, b)
175 179 if debug:
176 180 dbg('debug.copies: search limit: %d\n' % limit)
177 181 am = a.manifest()
178 182
179 183 # find where new files came from
180 184 # we currently don't try to find where old files went, too expensive
181 185 # this means we can miss a case like 'hg rm b; hg cp a b'
182 186 cm = {}
183 187
184 188 # Computing the forward missing is quite expensive on large manifests, since
185 189 # it compares the entire manifests. We can optimize it in the common use
186 190 # case of computing what copies are in a commit versus its parent (like
187 191 # during a rebase or histedit). Note, we exclude merge commits from this
188 192 # optimization, since the ctx.files() for a merge commit is not correct for
189 193 # this comparison.
190 194 forwardmissingmatch = match
191 195 if b.p1() == a and b.p2().node() == node.nullid:
192 196 filesmatcher = scmutil.matchfiles(a._repo, b.files())
193 197 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
194 198 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
195 199
196 200 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
197 201
198 202 if debug:
199 203 dbg('debug.copies: missing file to search: %d\n' % len(missing))
200 204
201 205 for f in missing:
202 206 if debug:
203 207 dbg('debug.copies: tracing file: %s\n' % f)
204 208 fctx = b[f]
205 209 fctx._ancestrycontext = ancestrycontext
206 210
207 211 if debug:
208 212 start = util.timer()
209 213 ofctx = _tracefile(fctx, am, limit)
210 214 if ofctx:
211 215 if debug:
212 216 dbg('debug.copies: rename of: %s\n' % ofctx._path)
213 217 cm[f] = ofctx.path()
214 218 if debug:
215 219 dbg('debug.copies: time: %f seconds\n'
216 220 % (util.timer() - start))
217 221 return cm
218 222
223 def _changesetforwardcopies(a, b, match):
224 if a.rev() == node.nullrev:
225 return {}
226
227 repo = a.repo()
228 children = {}
229 cl = repo.changelog
230 missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
231 for r in missingrevs:
232 for p in cl.parentrevs(r):
233 if p == node.nullrev:
234 continue
235 if p not in children:
236 children[p] = [r]
237 else:
238 children[p].append(r)
239
240 roots = set(children) - set(missingrevs)
241 # 'work' contains 3-tuples of a (revision number, parent number, copies).
242 # The parent number is only used for knowing which parent the copies dict
243 # came from.
244 work = [(r, 1, {}) for r in roots]
245 heapq.heapify(work)
246 while work:
247 r, i1, copies1 = heapq.heappop(work)
248 if work and work[0][0] == r:
249 # We are tracing copies from both parents
250 r, i2, copies2 = heapq.heappop(work)
251 copies = {}
252 ctx = repo[r]
253 p1man, p2man = ctx.p1().manifest(), ctx.p2().manifest()
254 allcopies = set(copies1) | set(copies2)
255 # TODO: perhaps this filtering should be done as long as ctx
256 # is merge, whether or not we're tracing from both parent.
257 for dst in allcopies:
258 if not match(dst):
259 continue
260 if dst not in copies2:
261 # Copied on p1 side: mark as copy from p1 side if it didn't
262 # already exist on p2 side
263 if dst not in p2man:
264 copies[dst] = copies1[dst]
265 elif dst not in copies1:
266 # Copied on p2 side: mark as copy from p2 side if it didn't
267 # already exist on p1 side
268 if dst not in p1man:
269 copies[dst] = copies2[dst]
270 else:
271 # Copied on both sides: mark as copy from p1 side
272 copies[dst] = copies1[dst]
273 else:
274 copies = copies1
275 if r == b.rev():
276 return copies
277 for c in children[r]:
278 childctx = repo[c]
279 if r == childctx.p1().rev():
280 parent = 1
281 childcopies = childctx.p1copies()
282 else:
283 assert r == childctx.p2().rev()
284 parent = 2
285 childcopies = childctx.p2copies()
286 if not match.always():
287 childcopies = {dst: src for dst, src in childcopies.items()
288 if match(dst)}
289 childcopies = _chain(a, childctx, copies, childcopies)
290 heapq.heappush(work, (c, parent, childcopies))
291 assert False
292
219 293 def _forwardcopies(a, b, match=None):
220 294 """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
221 295
222 296 match = a.repo().narrowmatch(match)
223 297 # check for working copy
224 298 if b.rev() is None:
225 299 if a == b.p1():
226 300 # short-circuit to avoid issues with merge states
227 301 return _dirstatecopies(b._repo, match)
228 302
229 303 cm = _committedforwardcopies(a, b.p1(), match)
230 304 # combine copies from dirstate if necessary
231 305 return _chain(a, b, cm, _dirstatecopies(b._repo, match))
232 306 return _committedforwardcopies(a, b, match)
233 307
234 308 def _backwardrenames(a, b, match):
235 309 if a._repo.ui.config('experimental', 'copytrace') == 'off':
236 310 return {}
237 311
238 312 # Even though we're not taking copies into account, 1:n rename situations
239 313 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
240 314 # arbitrarily pick one of the renames.
241 315 # We don't want to pass in "match" here, since that would filter
242 316 # the destination by it. Since we're reversing the copies, we want
243 317 # to filter the source instead.
244 318 f = _forwardcopies(b, a)
245 319 r = {}
246 320 for k, v in sorted(f.iteritems()):
247 321 if match and not match(v):
248 322 continue
249 323 # remove copies
250 324 if v in a:
251 325 continue
252 326 r[v] = k
253 327 return r
254 328
255 329 def pathcopies(x, y, match=None):
256 330 """find {dst@y: src@x} copy mapping for directed compare"""
257 331 repo = x._repo
258 332 debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies')
259 333 if debug:
260 334 repo.ui.debug('debug.copies: searching copies from %s to %s\n'
261 335 % (x, y))
262 336 if x == y or not x or not y:
263 337 return {}
264 338 a = y.ancestor(x)
265 339 if a == x:
266 340 if debug:
267 341 repo.ui.debug('debug.copies: search mode: forward\n')
268 342 return _forwardcopies(x, y, match=match)
269 343 if a == y:
270 344 if debug:
271 345 repo.ui.debug('debug.copies: search mode: backward\n')
272 346 return _backwardrenames(x, y, match=match)
273 347 if debug:
274 348 repo.ui.debug('debug.copies: search mode: combined\n')
275 349 return _chain(x, y, _backwardrenames(x, a, match=match),
276 350 _forwardcopies(a, y, match=match))
277 351
278 352 def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
279 353 """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
280 354 and c2. This is its own function so extensions can easily wrap this call
281 355 to see what files mergecopies is about to process.
282 356
283 357 Even though c1 and c2 are not used in this function, they are useful in
284 358 other extensions for being able to read the file nodes of the changed files.
285 359
286 360 "baselabel" can be passed to help distinguish the multiple computations
287 361 done in the graft case.
288 362 """
289 363 u1 = sorted(addedinm1 - addedinm2)
290 364 u2 = sorted(addedinm2 - addedinm1)
291 365
292 366 header = " unmatched files in %s"
293 367 if baselabel:
294 368 header += ' (from %s)' % baselabel
295 369 if u1:
296 370 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1)))
297 371 if u2:
298 372 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2)))
299 373
300 374 return u1, u2
301 375
302 376 def _makegetfctx(ctx):
303 377 """return a 'getfctx' function suitable for _checkcopies usage
304 378
305 379 We have to re-setup the function building 'filectx' for each
306 380 '_checkcopies' to ensure the linkrev adjustment is properly setup for
307 381 each. Linkrev adjustment is important to avoid bug in rename
308 382 detection. Moreover, having a proper '_ancestrycontext' setup ensures
309 383 the performance impact of this adjustment is kept limited. Without it,
310 384 each file could do a full dag traversal making the time complexity of
311 385 the operation explode (see issue4537).
312 386
313 387 This function exists here mostly to limit the impact on stable. Feel
314 388 free to refactor on default.
315 389 """
316 390 rev = ctx.rev()
317 391 repo = ctx._repo
318 392 ac = getattr(ctx, '_ancestrycontext', None)
319 393 if ac is None:
320 394 revs = [rev]
321 395 if rev is None:
322 396 revs = [p.rev() for p in ctx.parents()]
323 397 ac = repo.changelog.ancestors(revs, inclusive=True)
324 398 ctx._ancestrycontext = ac
325 399 def makectx(f, n):
326 400 if n in node.wdirfilenodeids: # in a working context?
327 401 if ctx.rev() is None:
328 402 return ctx.filectx(f)
329 403 return repo[None][f]
330 404 fctx = repo.filectx(f, fileid=n)
331 405 # setup only needed for filectx not create from a changectx
332 406 fctx._ancestrycontext = ac
333 407 fctx._descendantrev = rev
334 408 return fctx
335 409 return util.lrucachefunc(makectx)
336 410
337 411 def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge):
338 412 """combine partial copy paths"""
339 413 remainder = {}
340 414 for f in copyfrom:
341 415 if f in copyto:
342 416 finalcopy[copyto[f]] = copyfrom[f]
343 417 del copyto[f]
344 418 for f in incompletediverge:
345 419 assert f not in diverge
346 420 ic = incompletediverge[f]
347 421 if ic[0] in copyto:
348 422 diverge[f] = [copyto[ic[0]], ic[1]]
349 423 else:
350 424 remainder[f] = ic
351 425 return remainder
352 426
353 427 def mergecopies(repo, c1, c2, base):
354 428 """
355 429 The function calling different copytracing algorithms on the basis of config
356 430 which find moves and copies between context c1 and c2 that are relevant for
357 431 merging. 'base' will be used as the merge base.
358 432
359 433 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
360 434 files that were moved/ copied in one merge parent and modified in another.
361 435 For example:
362 436
363 437 o ---> 4 another commit
364 438 |
365 439 | o ---> 3 commit that modifies a.txt
366 440 | /
367 441 o / ---> 2 commit that moves a.txt to b.txt
368 442 |/
369 443 o ---> 1 merge base
370 444
371 445 If we try to rebase revision 3 on revision 4, since there is no a.txt in
372 446 revision 4, and if user have copytrace disabled, we prints the following
373 447 message:
374 448
375 449 ```other changed <file> which local deleted```
376 450
377 451 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
378 452 "dirmove".
379 453
380 454 "copy" is a mapping from destination name -> source name,
381 455 where source is in c1 and destination is in c2 or vice-versa.
382 456
383 457 "movewithdir" is a mapping from source name -> destination name,
384 458 where the file at source present in one context but not the other
385 459 needs to be moved to destination by the merge process, because the
386 460 other context moved the directory it is in.
387 461
388 462 "diverge" is a mapping of source name -> list of destination names
389 463 for divergent renames.
390 464
391 465 "renamedelete" is a mapping of source name -> list of destination
392 466 names for files deleted in c1 that were renamed in c2 or vice-versa.
393 467
394 468 "dirmove" is a mapping of detected source dir -> destination dir renames.
395 469 This is needed for handling changes to new files previously grafted into
396 470 renamed directories.
397 471 """
398 472 # avoid silly behavior for update from empty dir
399 473 if not c1 or not c2 or c1 == c2:
400 474 return {}, {}, {}, {}, {}
401 475
402 476 narrowmatch = c1.repo().narrowmatch()
403 477
404 478 # avoid silly behavior for parent -> working dir
405 479 if c2.node() is None and c1.node() == repo.dirstate.p1():
406 480 return _dirstatecopies(repo, narrowmatch), {}, {}, {}, {}
407 481
408 482 copytracing = repo.ui.config('experimental', 'copytrace')
409 483 boolctrace = stringutil.parsebool(copytracing)
410 484
411 485 # Copy trace disabling is explicitly below the node == p1 logic above
412 486 # because the logic above is required for a simple copy to be kept across a
413 487 # rebase.
414 488 if copytracing == 'heuristics':
415 489 # Do full copytracing if only non-public revisions are involved as
416 490 # that will be fast enough and will also cover the copies which could
417 491 # be missed by heuristics
418 492 if _isfullcopytraceable(repo, c1, base):
419 493 return _fullcopytracing(repo, c1, c2, base)
420 494 return _heuristicscopytracing(repo, c1, c2, base)
421 495 elif boolctrace is False:
422 496 # stringutil.parsebool() returns None when it is unable to parse the
423 497 # value, so we should rely on making sure copytracing is on such cases
424 498 return {}, {}, {}, {}, {}
425 499 else:
426 500 return _fullcopytracing(repo, c1, c2, base)
427 501
428 502 def _isfullcopytraceable(repo, c1, base):
429 503 """ Checks that if base, source and destination are all no-public branches,
430 504 if yes let's use the full copytrace algorithm for increased capabilities
431 505 since it will be fast enough.
432 506
433 507 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
434 508 number of changesets from c1 to base such that if number of changesets are
435 509 more than the limit, full copytracing algorithm won't be used.
436 510 """
437 511 if c1.rev() is None:
438 512 c1 = c1.p1()
439 513 if c1.mutable() and base.mutable():
440 514 sourcecommitlimit = repo.ui.configint('experimental',
441 515 'copytrace.sourcecommitlimit')
442 516 commits = len(repo.revs('%d::%d', base.rev(), c1.rev()))
443 517 return commits < sourcecommitlimit
444 518 return False
445 519
446 520 def _fullcopytracing(repo, c1, c2, base):
447 521 """ The full copytracing algorithm which finds all the new files that were
448 522 added from merge base up to the top commit and for each file it checks if
449 523 this file was copied from another file.
450 524
451 525 This is pretty slow when a lot of changesets are involved but will track all
452 526 the copies.
453 527 """
454 528 # In certain scenarios (e.g. graft, update or rebase), base can be
455 529 # overridden We still need to know a real common ancestor in this case We
456 530 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
457 531 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
458 532 # caller may not know if the revision passed in lieu of the CA is a genuine
459 533 # common ancestor or not without explicitly checking it, it's better to
460 534 # determine that here.
461 535 #
462 536 # base.isancestorof(wc) is False, work around that
463 537 _c1 = c1.p1() if c1.rev() is None else c1
464 538 _c2 = c2.p1() if c2.rev() is None else c2
465 539 # an endpoint is "dirty" if it isn't a descendant of the merge base
466 540 # if we have a dirty endpoint, we need to trigger graft logic, and also
467 541 # keep track of which endpoint is dirty
468 542 dirtyc1 = not base.isancestorof(_c1)
469 543 dirtyc2 = not base.isancestorof(_c2)
470 544 graft = dirtyc1 or dirtyc2
471 545 tca = base
472 546 if graft:
473 547 tca = _c1.ancestor(_c2)
474 548
475 549 limit = _findlimit(repo, c1, c2)
476 550 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
477 551
478 552 m1 = c1.manifest()
479 553 m2 = c2.manifest()
480 554 mb = base.manifest()
481 555
482 556 # gather data from _checkcopies:
483 557 # - diverge = record all diverges in this dict
484 558 # - copy = record all non-divergent copies in this dict
485 559 # - fullcopy = record all copies in this dict
486 560 # - incomplete = record non-divergent partial copies here
487 561 # - incompletediverge = record divergent partial copies here
488 562 diverge = {} # divergence data is shared
489 563 incompletediverge = {}
490 564 data1 = {'copy': {},
491 565 'fullcopy': {},
492 566 'incomplete': {},
493 567 'diverge': diverge,
494 568 'incompletediverge': incompletediverge,
495 569 }
496 570 data2 = {'copy': {},
497 571 'fullcopy': {},
498 572 'incomplete': {},
499 573 'diverge': diverge,
500 574 'incompletediverge': incompletediverge,
501 575 }
502 576
503 577 # find interesting file sets from manifests
504 578 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
505 579 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
506 580 bothnew = sorted(addedinm1 & addedinm2)
507 581 if tca == base:
508 582 # unmatched file from base
509 583 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
510 584 u1u, u2u = u1r, u2r
511 585 else:
512 586 # unmatched file from base (DAG rotation in the graft case)
513 587 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
514 588 baselabel='base')
515 589 # unmatched file from topological common ancestors (no DAG rotation)
516 590 # need to recompute this for directory move handling when grafting
517 591 mta = tca.manifest()
518 592 u1u, u2u = _computenonoverlap(repo, c1, c2,
519 593 m1.filesnotin(mta, repo.narrowmatch()),
520 594 m2.filesnotin(mta, repo.narrowmatch()),
521 595 baselabel='topological common ancestor')
522 596
523 597 for f in u1u:
524 598 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)
525 599
526 600 for f in u2u:
527 601 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)
528 602
529 603 copy = dict(data1['copy'])
530 604 copy.update(data2['copy'])
531 605 fullcopy = dict(data1['fullcopy'])
532 606 fullcopy.update(data2['fullcopy'])
533 607
534 608 if dirtyc1:
535 609 _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge,
536 610 incompletediverge)
537 611 else:
538 612 _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge,
539 613 incompletediverge)
540 614
541 615 renamedelete = {}
542 616 renamedeleteset = set()
543 617 divergeset = set()
544 618 for of, fl in list(diverge.items()):
545 619 if len(fl) == 1 or of in c1 or of in c2:
546 620 del diverge[of] # not actually divergent, or not a rename
547 621 if of not in c1 and of not in c2:
548 622 # renamed on one side, deleted on the other side, but filter
549 623 # out files that have been renamed and then deleted
550 624 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
551 625 renamedeleteset.update(fl) # reverse map for below
552 626 else:
553 627 divergeset.update(fl) # reverse map for below
554 628
555 629 if bothnew:
556 630 repo.ui.debug(" unmatched files new in both:\n %s\n"
557 631 % "\n ".join(bothnew))
558 632 bothdiverge = {}
559 633 bothincompletediverge = {}
560 634 remainder = {}
561 635 both1 = {'copy': {},
562 636 'fullcopy': {},
563 637 'incomplete': {},
564 638 'diverge': bothdiverge,
565 639 'incompletediverge': bothincompletediverge
566 640 }
567 641 both2 = {'copy': {},
568 642 'fullcopy': {},
569 643 'incomplete': {},
570 644 'diverge': bothdiverge,
571 645 'incompletediverge': bothincompletediverge
572 646 }
573 647 for f in bothnew:
574 648 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)
575 649 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)
576 650 if dirtyc1:
577 651 # incomplete copies may only be found on the "dirty" side for bothnew
578 652 assert not both2['incomplete']
579 653 remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge,
580 654 bothincompletediverge)
581 655 elif dirtyc2:
582 656 assert not both1['incomplete']
583 657 remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge,
584 658 bothincompletediverge)
585 659 else:
586 660 # incomplete copies and divergences can't happen outside grafts
587 661 assert not both1['incomplete']
588 662 assert not both2['incomplete']
589 663 assert not bothincompletediverge
590 664 for f in remainder:
591 665 assert f not in bothdiverge
592 666 ic = remainder[f]
593 667 if ic[0] in (m1 if dirtyc1 else m2):
594 668 # backed-out rename on one side, but watch out for deleted files
595 669 bothdiverge[f] = ic
596 670 for of, fl in bothdiverge.items():
597 671 if len(fl) == 2 and fl[0] == fl[1]:
598 672 copy[fl[0]] = of # not actually divergent, just matching renames
599 673
600 674 if fullcopy and repo.ui.debugflag:
601 675 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
602 676 "% = renamed and deleted):\n")
603 677 for f in sorted(fullcopy):
604 678 note = ""
605 679 if f in copy:
606 680 note += "*"
607 681 if f in divergeset:
608 682 note += "!"
609 683 if f in renamedeleteset:
610 684 note += "%"
611 685 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
612 686 note))
613 687 del divergeset
614 688
615 689 if not fullcopy:
616 690 return copy, {}, diverge, renamedelete, {}
617 691
618 692 repo.ui.debug(" checking for directory renames\n")
619 693
620 694 # generate a directory move map
621 695 d1, d2 = c1.dirs(), c2.dirs()
622 696 # Hack for adding '', which is not otherwise added, to d1 and d2
623 697 d1.addpath('/')
624 698 d2.addpath('/')
625 699 invalid = set()
626 700 dirmove = {}
627 701
628 702 # examine each file copy for a potential directory move, which is
629 703 # when all the files in a directory are moved to a new directory
630 704 for dst, src in fullcopy.iteritems():
631 705 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
632 706 if dsrc in invalid:
633 707 # already seen to be uninteresting
634 708 continue
635 709 elif dsrc in d1 and ddst in d1:
636 710 # directory wasn't entirely moved locally
637 711 invalid.add(dsrc)
638 712 elif dsrc in d2 and ddst in d2:
639 713 # directory wasn't entirely moved remotely
640 714 invalid.add(dsrc)
641 715 elif dsrc in dirmove and dirmove[dsrc] != ddst:
642 716 # files from the same directory moved to two different places
643 717 invalid.add(dsrc)
644 718 else:
645 719 # looks good so far
646 720 dirmove[dsrc] = ddst
647 721
648 722 for i in invalid:
649 723 if i in dirmove:
650 724 del dirmove[i]
651 725 del d1, d2, invalid
652 726
653 727 if not dirmove:
654 728 return copy, {}, diverge, renamedelete, {}
655 729
656 730 dirmove = {k + "/": v + "/" for k, v in dirmove.iteritems()}
657 731
658 732 for d in dirmove:
659 733 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
660 734 (d, dirmove[d]))
661 735
662 736 movewithdir = {}
663 737 # check unaccounted nonoverlapping files against directory moves
664 738 for f in u1r + u2r:
665 739 if f not in fullcopy:
666 740 for d in dirmove:
667 741 if f.startswith(d):
668 742 # new file added in a directory that was moved, move it
669 743 df = dirmove[d] + f[len(d):]
670 744 if df not in copy:
671 745 movewithdir[f] = df
672 746 repo.ui.debug((" pending file src: '%s' -> "
673 747 "dst: '%s'\n") % (f, df))
674 748 break
675 749
676 750 return copy, movewithdir, diverge, renamedelete, dirmove
677 751
678 752 def _heuristicscopytracing(repo, c1, c2, base):
679 753 """ Fast copytracing using filename heuristics
680 754
681 755 Assumes that moves or renames are of following two types:
682 756
683 757 1) Inside a directory only (same directory name but different filenames)
684 758 2) Move from one directory to another
685 759 (same filenames but different directory names)
686 760
687 761 Works only when there are no merge commits in the "source branch".
688 762 Source branch is commits from base up to c2 not including base.
689 763
690 764 If merge is involved it fallbacks to _fullcopytracing().
691 765
692 766 Can be used by setting the following config:
693 767
694 768 [experimental]
695 769 copytrace = heuristics
696 770
697 771 In some cases the copy/move candidates found by heuristics can be very large
698 772 in number and that will make the algorithm slow. The number of possible
699 773 candidates to check can be limited by using the config
700 774 `experimental.copytrace.movecandidateslimit` which defaults to 100.
701 775 """
702 776
703 777 if c1.rev() is None:
704 778 c1 = c1.p1()
705 779 if c2.rev() is None:
706 780 c2 = c2.p1()
707 781
708 782 copies = {}
709 783
710 784 changedfiles = set()
711 785 m1 = c1.manifest()
712 786 if not repo.revs('%d::%d', base.rev(), c2.rev()):
713 787 # If base is not in c2 branch, we switch to fullcopytracing
714 788 repo.ui.debug("switching to full copytracing as base is not "
715 789 "an ancestor of c2\n")
716 790 return _fullcopytracing(repo, c1, c2, base)
717 791
718 792 ctx = c2
719 793 while ctx != base:
720 794 if len(ctx.parents()) == 2:
721 795 # To keep things simple let's not handle merges
722 796 repo.ui.debug("switching to full copytracing because of merges\n")
723 797 return _fullcopytracing(repo, c1, c2, base)
724 798 changedfiles.update(ctx.files())
725 799 ctx = ctx.p1()
726 800
727 801 cp = _forwardcopies(base, c2)
728 802 for dst, src in cp.iteritems():
729 803 if src in m1:
730 804 copies[dst] = src
731 805
732 806 # file is missing if it isn't present in the destination, but is present in
733 807 # the base and present in the source.
734 808 # Presence in the base is important to exclude added files, presence in the
735 809 # source is important to exclude removed files.
736 810 filt = lambda f: f not in m1 and f in base and f in c2
737 811 missingfiles = [f for f in changedfiles if filt(f)]
738 812
739 813 if missingfiles:
740 814 basenametofilename = collections.defaultdict(list)
741 815 dirnametofilename = collections.defaultdict(list)
742 816
743 817 for f in m1.filesnotin(base.manifest()):
744 818 basename = os.path.basename(f)
745 819 dirname = os.path.dirname(f)
746 820 basenametofilename[basename].append(f)
747 821 dirnametofilename[dirname].append(f)
748 822
749 823 for f in missingfiles:
750 824 basename = os.path.basename(f)
751 825 dirname = os.path.dirname(f)
752 826 samebasename = basenametofilename[basename]
753 827 samedirname = dirnametofilename[dirname]
754 828 movecandidates = samebasename + samedirname
755 829 # f is guaranteed to be present in c2, that's why
756 830 # c2.filectx(f) won't fail
757 831 f2 = c2.filectx(f)
758 832 # we can have a lot of candidates which can slow down the heuristics
759 833 # config value to limit the number of candidates moves to check
760 834 maxcandidates = repo.ui.configint('experimental',
761 835 'copytrace.movecandidateslimit')
762 836
763 837 if len(movecandidates) > maxcandidates:
764 838 repo.ui.status(_("skipping copytracing for '%s', more "
765 839 "candidates than the limit: %d\n")
766 840 % (f, len(movecandidates)))
767 841 continue
768 842
769 843 for candidate in movecandidates:
770 844 f1 = c1.filectx(candidate)
771 845 if _related(f1, f2):
772 846 # if there are a few related copies then we'll merge
773 847 # changes into all of them. This matches the behaviour
774 848 # of upstream copytracing
775 849 copies[candidate] = f
776 850
777 851 return copies, {}, {}, {}, {}
778 852
779 853 def _related(f1, f2):
780 854 """return True if f1 and f2 filectx have a common ancestor
781 855
782 856 Walk back to common ancestor to see if the two files originate
783 857 from the same file. Since workingfilectx's rev() is None it messes
784 858 up the integer comparison logic, hence the pre-step check for
785 859 None (f1 and f2 can only be workingfilectx's initially).
786 860 """
787 861
788 862 if f1 == f2:
789 863 return True # a match
790 864
791 865 g1, g2 = f1.ancestors(), f2.ancestors()
792 866 try:
793 867 f1r, f2r = f1.linkrev(), f2.linkrev()
794 868
795 869 if f1r is None:
796 870 f1 = next(g1)
797 871 if f2r is None:
798 872 f2 = next(g2)
799 873
800 874 while True:
801 875 f1r, f2r = f1.linkrev(), f2.linkrev()
802 876 if f1r > f2r:
803 877 f1 = next(g1)
804 878 elif f2r > f1r:
805 879 f2 = next(g2)
806 880 else: # f1 and f2 point to files in the same linkrev
807 881 return f1 == f2 # true if they point to the same file
808 882 except StopIteration:
809 883 return False
810 884
811 885 def _checkcopies(srcctx, dstctx, f, base, tca, remotebase, limit, data):
812 886 """
813 887 check possible copies of f from msrc to mdst
814 888
815 889 srcctx = starting context for f in msrc
816 890 dstctx = destination context for f in mdst
817 891 f = the filename to check (as in msrc)
818 892 base = the changectx used as a merge base
819 893 tca = topological common ancestor for graft-like scenarios
820 894 remotebase = True if base is outside tca::srcctx, False otherwise
821 895 limit = the rev number to not search beyond
822 896 data = dictionary of dictionary to store copy data. (see mergecopies)
823 897
824 898 note: limit is only an optimization, and provides no guarantee that
825 899 irrelevant revisions will not be visited
826 900 there is no easy way to make this algorithm stop in a guaranteed way
827 901 once it "goes behind a certain revision".
828 902 """
829 903
830 904 msrc = srcctx.manifest()
831 905 mdst = dstctx.manifest()
832 906 mb = base.manifest()
833 907 mta = tca.manifest()
834 908 # Might be true if this call is about finding backward renames,
835 909 # This happens in the case of grafts because the DAG is then rotated.
836 910 # If the file exists in both the base and the source, we are not looking
837 911 # for a rename on the source side, but on the part of the DAG that is
838 912 # traversed backwards.
839 913 #
840 914 # In the case there is both backward and forward renames (before and after
841 915 # the base) this is more complicated as we must detect a divergence.
842 916 # We use 'backwards = False' in that case.
843 917 backwards = not remotebase and base != tca and f in mb
844 918 getsrcfctx = _makegetfctx(srcctx)
845 919 getdstfctx = _makegetfctx(dstctx)
846 920
847 921 if msrc[f] == mb.get(f) and not remotebase:
848 922 # Nothing to merge
849 923 return
850 924
851 925 of = None
852 926 seen = {f}
853 927 for oc in getsrcfctx(f, msrc[f]).ancestors():
854 928 of = oc.path()
855 929 if of in seen:
856 930 # check limit late - grab last rename before
857 931 if oc.linkrev() < limit:
858 932 break
859 933 continue
860 934 seen.add(of)
861 935
862 936 # remember for dir rename detection
863 937 if backwards:
864 938 data['fullcopy'][of] = f # grafting backwards through renames
865 939 else:
866 940 data['fullcopy'][f] = of
867 941 if of not in mdst:
868 942 continue # no match, keep looking
869 943 if mdst[of] == mb.get(of):
870 944 return # no merge needed, quit early
871 945 c2 = getdstfctx(of, mdst[of])
872 946 # c2 might be a plain new file on added on destination side that is
873 947 # unrelated to the droids we are looking for.
874 948 cr = _related(oc, c2)
875 949 if cr and (of == f or of == c2.path()): # non-divergent
876 950 if backwards:
877 951 data['copy'][of] = f
878 952 elif of in mb:
879 953 data['copy'][f] = of
880 954 elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename
881 955 data['copy'][of] = f
882 956 del data['fullcopy'][f]
883 957 data['fullcopy'][of] = f
884 958 else: # divergence w.r.t. graft CA on one side of topological CA
885 959 for sf in seen:
886 960 if sf in mb:
887 961 assert sf not in data['diverge']
888 962 data['diverge'][sf] = [f, of]
889 963 break
890 964 return
891 965
892 966 if of in mta:
893 967 if backwards or remotebase:
894 968 data['incomplete'][of] = f
895 969 else:
896 970 for sf in seen:
897 971 if sf in mb:
898 972 if tca == base:
899 973 data['diverge'].setdefault(sf, []).append(f)
900 974 else:
901 975 data['incompletediverge'][sf] = [of, f]
902 976 return
903 977
904 978 def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None):
905 979 """reproduce copies from fromrev to rev in the dirstate
906 980
907 981 If skiprev is specified, it's a revision that should be used to
908 982 filter copy records. Any copies that occur between fromrev and
909 983 skiprev will not be duplicated, even if they appear in the set of
910 984 copies between fromrev and rev.
911 985 """
912 986 exclude = {}
913 987 ctraceconfig = repo.ui.config('experimental', 'copytrace')
914 988 bctrace = stringutil.parsebool(ctraceconfig)
915 989 if (skiprev is not None and
916 990 (ctraceconfig == 'heuristics' or bctrace or bctrace is None)):
917 991 # copytrace='off' skips this line, but not the entire function because
918 992 # the line below is O(size of the repo) during a rebase, while the rest
919 993 # of the function is much faster (and is required for carrying copy
920 994 # metadata across the rebase anyway).
921 995 exclude = pathcopies(repo[fromrev], repo[skiprev])
922 996 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
923 997 # copies.pathcopies returns backward renames, so dst might not
924 998 # actually be in the dirstate
925 999 if dst in exclude:
926 1000 continue
927 1001 wctx[dst].markcopied(src)
@@ -1,484 +1,493 b''
1 #testcases filelog compatibility
1 2
2 3 $ cat >> $HGRCPATH << EOF
3 4 > [alias]
4 5 > l = log -G -T '{rev} {desc}\n{files}\n'
5 6 > EOF
6 7
8 #if compatibility
9 $ cat >> $HGRCPATH << EOF
10 > [experimental]
11 > copies.read-from = compatibility
12 > EOF
13 #endif
14
7 15 $ REPONUM=0
8 16 $ newrepo() {
9 17 > cd $TESTTMP
10 18 > REPONUM=`expr $REPONUM + 1`
11 19 > hg init repo-$REPONUM
12 20 > cd repo-$REPONUM
13 21 > }
14 22
15 23 Simple rename case
16 24 $ newrepo
17 25 $ echo x > x
18 26 $ hg ci -Aqm 'add x'
19 27 $ hg mv x y
20 28 $ hg debugp1copies
21 29 x -> y
22 30 $ hg debugp2copies
23 31 $ hg ci -m 'rename x to y'
24 32 $ hg l
25 33 @ 1 rename x to y
26 34 | x y
27 35 o 0 add x
28 36 x
29 37 $ hg debugp1copies -r 1
30 38 x -> y
31 39 $ hg debugpathcopies 0 1
32 40 x -> y
33 41 $ hg debugpathcopies 1 0
34 42 y -> x
35 43 Test filtering copies by path. We do filtering by destination.
36 44 $ hg debugpathcopies 0 1 x
37 45 $ hg debugpathcopies 1 0 x
38 46 y -> x
39 47 $ hg debugpathcopies 0 1 y
40 48 x -> y
41 49 $ hg debugpathcopies 1 0 y
42 50
43 51 Copy a file onto another file
44 52 $ newrepo
45 53 $ echo x > x
46 54 $ echo y > y
47 55 $ hg ci -Aqm 'add x and y'
48 56 $ hg cp -f x y
49 57 $ hg debugp1copies
50 58 x -> y
51 59 $ hg debugp2copies
52 60 $ hg ci -m 'copy x onto y'
53 61 $ hg l
54 62 @ 1 copy x onto y
55 63 | y
56 64 o 0 add x and y
57 65 x y
58 66 $ hg debugp1copies -r 1
59 67 x -> y
60 68 Incorrectly doesn't show the rename
61 69 $ hg debugpathcopies 0 1
62 70
63 71 Copy a file onto another file with same content. If metadata is stored in changeset, this does not
64 72 produce a new filelog entry. The changeset's "files" entry should still list the file.
65 73 $ newrepo
66 74 $ echo x > x
67 75 $ echo x > x2
68 76 $ hg ci -Aqm 'add x and x2 with same content'
69 77 $ hg cp -f x x2
70 78 $ hg ci -m 'copy x onto x2'
71 79 $ hg l
72 80 @ 1 copy x onto x2
73 81 | x2
74 82 o 0 add x and x2 with same content
75 83 x x2
76 84 $ hg debugp1copies -r 1
77 85 x -> x2
78 86 Incorrectly doesn't show the rename
79 87 $ hg debugpathcopies 0 1
80 88
81 89 Copy a file, then delete destination, then copy again. This does not create a new filelog entry.
82 90 $ newrepo
83 91 $ echo x > x
84 92 $ hg ci -Aqm 'add x'
85 93 $ hg cp x y
86 94 $ hg ci -m 'copy x to y'
87 95 $ hg rm y
88 96 $ hg ci -m 'remove y'
89 97 $ hg cp -f x y
90 98 $ hg ci -m 'copy x onto y (again)'
91 99 $ hg l
92 100 @ 3 copy x onto y (again)
93 101 | y
94 102 o 2 remove y
95 103 | y
96 104 o 1 copy x to y
97 105 | y
98 106 o 0 add x
99 107 x
100 108 $ hg debugp1copies -r 3
101 109 x -> y
102 110 $ hg debugpathcopies 0 3
103 111 x -> y
104 112
105 113 Rename file in a loop: x->y->z->x
106 114 $ newrepo
107 115 $ echo x > x
108 116 $ hg ci -Aqm 'add x'
109 117 $ hg mv x y
110 118 $ hg debugp1copies
111 119 x -> y
112 120 $ hg debugp2copies
113 121 $ hg ci -m 'rename x to y'
114 122 $ hg mv y z
115 123 $ hg ci -m 'rename y to z'
116 124 $ hg mv z x
117 125 $ hg ci -m 'rename z to x'
118 126 $ hg l
119 127 @ 3 rename z to x
120 128 | x z
121 129 o 2 rename y to z
122 130 | y z
123 131 o 1 rename x to y
124 132 | x y
125 133 o 0 add x
126 134 x
127 135 $ hg debugpathcopies 0 3
128 136
129 137 Copy x to y, then remove y, then add back y. With copy metadata in the changeset, this could easily
130 138 end up reporting y as copied from x (if we don't unmark it as a copy when it's removed).
131 139 $ newrepo
132 140 $ echo x > x
133 141 $ hg ci -Aqm 'add x'
134 142 $ hg mv x y
135 143 $ hg ci -m 'rename x to y'
136 144 $ hg rm y
137 145 $ hg ci -qm 'remove y'
138 146 $ echo x > y
139 147 $ hg ci -Aqm 'add back y'
140 148 $ hg l
141 149 @ 3 add back y
142 150 | y
143 151 o 2 remove y
144 152 | y
145 153 o 1 rename x to y
146 154 | x y
147 155 o 0 add x
148 156 x
149 157 $ hg debugp1copies -r 3
150 158 $ hg debugpathcopies 0 3
151 159
152 160 Copy x to z, then remove z, then copy x2 (same content as x) to z. With copy metadata in the
153 161 changeset, the two copies here will have the same filelog entry, so ctx['z'].introrev() might point
154 162 to the first commit that added the file. We should still report the copy as being from x2.
155 163 $ newrepo
156 164 $ echo x > x
157 165 $ echo x > x2
158 166 $ hg ci -Aqm 'add x and x2 with same content'
159 167 $ hg cp x z
160 168 $ hg ci -qm 'copy x to z'
161 169 $ hg rm z
162 170 $ hg ci -m 'remove z'
163 171 $ hg cp x2 z
164 172 $ hg ci -m 'copy x2 to z'
165 173 $ hg l
166 174 @ 3 copy x2 to z
167 175 | z
168 176 o 2 remove z
169 177 | z
170 178 o 1 copy x to z
171 179 | z
172 180 o 0 add x and x2 with same content
173 181 x x2
174 182 $ hg debugp1copies -r 3
175 183 x2 -> z
176 184 $ hg debugpathcopies 0 3
177 185 x2 -> z
178 186
179 187 Create x and y, then rename them both to the same name, but on different sides of a fork
180 188 $ newrepo
181 189 $ echo x > x
182 190 $ echo y > y
183 191 $ hg ci -Aqm 'add x and y'
184 192 $ hg mv x z
185 193 $ hg ci -qm 'rename x to z'
186 194 $ hg co -q 0
187 195 $ hg mv y z
188 196 $ hg ci -qm 'rename y to z'
189 197 $ hg l
190 198 @ 2 rename y to z
191 199 | y z
192 200 | o 1 rename x to z
193 201 |/ x z
194 202 o 0 add x and y
195 203 x y
196 204 $ hg debugpathcopies 1 2
197 205 z -> x
198 206 y -> z
199 207
200 208 Fork renames x to y on one side and removes x on the other
201 209 $ newrepo
202 210 $ echo x > x
203 211 $ hg ci -Aqm 'add x'
204 212 $ hg mv x y
205 213 $ hg ci -m 'rename x to y'
206 214 $ hg co -q 0
207 215 $ hg rm x
208 216 $ hg ci -m 'remove x'
209 217 created new head
210 218 $ hg l
211 219 @ 2 remove x
212 220 | x
213 221 | o 1 rename x to y
214 222 |/ x y
215 223 o 0 add x
216 224 x
217 225 $ hg debugpathcopies 1 2
218 226
219 227 Copies via null revision (there shouldn't be any)
220 228 $ newrepo
221 229 $ echo x > x
222 230 $ hg ci -Aqm 'add x'
223 231 $ hg cp x y
224 232 $ hg ci -m 'copy x to y'
225 233 $ hg co -q null
226 234 $ echo x > x
227 235 $ hg ci -Aqm 'add x (again)'
228 236 $ hg l
229 237 @ 2 add x (again)
230 238 x
231 239 o 1 copy x to y
232 240 | y
233 241 o 0 add x
234 242 x
235 243 $ hg debugpathcopies 1 2
236 244 $ hg debugpathcopies 2 1
237 245
238 246 Merge rename from other branch
239 247 $ newrepo
240 248 $ echo x > x
241 249 $ hg ci -Aqm 'add x'
242 250 $ hg mv x y
243 251 $ hg ci -m 'rename x to y'
244 252 $ hg co -q 0
245 253 $ echo z > z
246 254 $ hg ci -Aqm 'add z'
247 255 $ hg merge -q 1
248 256 $ hg debugp1copies
249 257 $ hg debugp2copies
250 258 $ hg ci -m 'merge rename from p2'
251 259 $ hg l
252 260 @ 3 merge rename from p2
253 261 |\ x
254 262 | o 2 add z
255 263 | | z
256 264 o | 1 rename x to y
257 265 |/ x y
258 266 o 0 add x
259 267 x
260 268 Perhaps we should indicate the rename here, but `hg status` is documented to be weird during
261 269 merges, so...
262 270 $ hg debugp1copies -r 3
263 271 $ hg debugp2copies -r 3
264 272 $ hg debugpathcopies 0 3
265 273 x -> y
266 274 $ hg debugpathcopies 1 2
267 275 y -> x
268 276 $ hg debugpathcopies 1 3
269 277 $ hg debugpathcopies 2 3
270 278 x -> y
271 279
272 280 Copy file from either side in a merge
273 281 $ newrepo
274 282 $ echo x > x
275 283 $ hg ci -Aqm 'add x'
276 284 $ hg co -q null
277 285 $ echo y > y
278 286 $ hg ci -Aqm 'add y'
279 287 $ hg merge -q 0
280 288 $ hg cp y z
281 289 $ hg debugp1copies
282 290 y -> z
283 291 $ hg debugp2copies
284 292 $ hg ci -m 'copy file from p1 in merge'
285 293 $ hg co -q 1
286 294 $ hg merge -q 0
287 295 $ hg cp x z
288 296 $ hg debugp1copies
289 297 $ hg debugp2copies
290 298 x -> z
291 299 $ hg ci -qm 'copy file from p2 in merge'
292 300 $ hg l
293 301 @ 3 copy file from p2 in merge
294 302 |\ z
295 303 +---o 2 copy file from p1 in merge
296 304 | |/ z
297 305 | o 1 add y
298 306 | y
299 307 o 0 add x
300 308 x
301 309 $ hg debugp1copies -r 2
302 310 y -> z
303 311 $ hg debugp2copies -r 2
304 312 $ hg debugpathcopies 1 2
305 313 y -> z
306 314 $ hg debugpathcopies 0 2
307 315 $ hg debugp1copies -r 3
308 316 $ hg debugp2copies -r 3
309 317 x -> z
310 318 $ hg debugpathcopies 1 3
311 319 $ hg debugpathcopies 0 3
312 320 x -> z
313 321
314 322 Copy file that exists on both sides of the merge, same content on both sides
315 323 $ newrepo
316 324 $ echo x > x
317 325 $ hg ci -Aqm 'add x on branch 1'
318 326 $ hg co -q null
319 327 $ echo x > x
320 328 $ hg ci -Aqm 'add x on branch 2'
321 329 $ hg merge -q 0
322 330 $ hg cp x z
323 331 $ hg debugp1copies
324 332 x -> z
325 333 $ hg debugp2copies
326 334 $ hg ci -qm 'merge'
327 335 $ hg l
328 336 @ 2 merge
329 337 |\ z
330 338 | o 1 add x on branch 2
331 339 | x
332 340 o 0 add x on branch 1
333 341 x
334 342 $ hg debugp1copies -r 2
335 343 x -> z
336 344 $ hg debugp2copies -r 2
337 345 It's a little weird that it shows up on both sides
338 346 $ hg debugpathcopies 1 2
339 347 x -> z
340 348 $ hg debugpathcopies 0 2
341 x -> z
349 x -> z (filelog !)
342 350
343 351 Copy file that exists on both sides of the merge, different content
344 352 $ newrepo
345 353 $ echo branch1 > x
346 354 $ hg ci -Aqm 'add x on branch 1'
347 355 $ hg co -q null
348 356 $ echo branch2 > x
349 357 $ hg ci -Aqm 'add x on branch 2'
350 358 $ hg merge -q 0
351 359 warning: conflicts while merging x! (edit, then use 'hg resolve --mark')
352 360 [1]
353 361 $ echo resolved > x
354 362 $ hg resolve -m x
355 363 (no more unresolved files)
356 364 $ hg cp x z
357 365 $ hg debugp1copies
358 366 x -> z
359 367 $ hg debugp2copies
360 368 $ hg ci -qm 'merge'
361 369 $ hg l
362 370 @ 2 merge
363 371 |\ x z
364 372 | o 1 add x on branch 2
365 373 | x
366 374 o 0 add x on branch 1
367 375 x
368 376 $ hg debugp1copies -r 2
369 377 $ hg debugp2copies -r 2
370 378 x -> z
371 379 $ hg debugpathcopies 1 2
372 380 $ hg debugpathcopies 0 2
373 381 x -> z
374 382
375 383 Copy x->y on one side of merge and copy x->z on the other side. Pathcopies from one parent
376 384 of the merge to the merge should include the copy from the other side.
377 385 $ newrepo
378 386 $ echo x > x
379 387 $ hg ci -Aqm 'add x'
380 388 $ hg cp x y
381 389 $ hg ci -qm 'copy x to y'
382 390 $ hg co -q 0
383 391 $ hg cp x z
384 392 $ hg ci -qm 'copy x to z'
385 393 $ hg merge -q 1
386 394 $ hg ci -m 'merge copy x->y and copy x->z'
387 395 $ hg l
388 396 @ 3 merge copy x->y and copy x->z
389 397 |\
390 398 | o 2 copy x to z
391 399 | | z
392 400 o | 1 copy x to y
393 401 |/ y
394 402 o 0 add x
395 403 x
396 404 $ hg debugp1copies -r 3
397 405 $ hg debugp2copies -r 3
398 406 $ hg debugpathcopies 2 3
399 407 x -> y
400 408 $ hg debugpathcopies 1 3
401 409 x -> z
402 410
403 411 Copy x to y on one side of merge, create y and rename to z on the other side. Pathcopies from the
404 412 first side should not include the y->z rename since y didn't exist in the merge base.
405 413 $ newrepo
406 414 $ echo x > x
407 415 $ hg ci -Aqm 'add x'
408 416 $ hg cp x y
409 417 $ hg ci -qm 'copy x to y'
410 418 $ hg co -q 0
411 419 $ echo y > y
412 420 $ hg ci -Aqm 'add y'
413 421 $ hg mv y z
414 422 $ hg ci -m 'rename y to z'
415 423 $ hg merge -q 1
416 424 $ hg ci -m 'merge'
417 425 $ hg l
418 426 @ 4 merge
419 427 |\
420 428 | o 3 rename y to z
421 429 | | y z
422 430 | o 2 add y
423 431 | | y
424 432 o | 1 copy x to y
425 433 |/ y
426 434 o 0 add x
427 435 x
428 436 $ hg debugp1copies -r 3
429 437 y -> z
430 438 $ hg debugp2copies -r 3
431 439 $ hg debugpathcopies 2 3
432 440 y -> z
433 441 $ hg debugpathcopies 1 3
434 442
435 443 Create x and y, then rename x to z on one side of merge, and rename y to z and modify z on the
436 444 other side.
437 445 $ newrepo
438 446 $ echo x > x
439 447 $ echo y > y
440 448 $ hg ci -Aqm 'add x and y'
441 449 $ hg mv x z
442 450 $ hg ci -qm 'rename x to z'
443 451 $ hg co -q 0
444 452 $ hg mv y z
445 453 $ hg ci -qm 'rename y to z'
446 454 $ echo z >> z
447 455 $ hg ci -m 'modify z'
448 456 $ hg merge -q 1
449 457 warning: conflicts while merging z! (edit, then use 'hg resolve --mark')
450 458 [1]
451 459 $ echo z > z
452 460 $ hg resolve -qm z
453 461 $ hg ci -m 'merge 1 into 3'
454 462 Try merging the other direction too
455 463 $ hg co -q 1
456 464 $ hg merge -q 3
457 465 warning: conflicts while merging z! (edit, then use 'hg resolve --mark')
458 466 [1]
459 467 $ echo z > z
460 468 $ hg resolve -qm z
461 469 $ hg ci -m 'merge 3 into 1'
462 470 created new head
463 471 $ hg l
464 472 @ 5 merge 3 into 1
465 473 |\ y z
466 474 +---o 4 merge 1 into 3
467 475 | |/ x z
468 476 | o 3 modify z
469 477 | | z
470 478 | o 2 rename y to z
471 479 | | y z
472 480 o | 1 rename x to z
473 481 |/ x z
474 482 o 0 add x and y
475 483 x y
476 484 $ hg debugpathcopies 1 4
477 485 $ hg debugpathcopies 2 4
478 486 $ hg debugpathcopies 0 4
479 x -> z
487 x -> z (filelog !)
488 y -> z (compatibility !)
480 489 $ hg debugpathcopies 1 5
481 490 $ hg debugpathcopies 2 5
482 491 $ hg debugpathcopies 0 5
483 492 x -> z
484 493
General Comments 0
You need to be logged in to leave comments. Login now