##// END OF EJS Templates
copies: add a config to limit the number of candidates to check in heuristics...
Pulkit Goyal -
r34847:f05a6e01 default
parent child Browse files
Show More
@@ -1,1100 +1,1103 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import functools
11 11 import re
12 12
13 13 from . import (
14 14 encoding,
15 15 error,
16 16 )
17 17
18 18 def loadconfigtable(ui, extname, configtable):
19 19 """update config item known to the ui with the extension ones"""
20 20 for section, items in configtable.items():
21 21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 22 knownkeys = set(knownitems)
23 23 newkeys = set(items)
24 24 for key in sorted(knownkeys & newkeys):
25 25 msg = "extension '%s' overwrite config item '%s.%s'"
26 26 msg %= (extname, section, key)
27 27 ui.develwarn(msg, config='warn-config')
28 28
29 29 knownitems.update(items)
30 30
31 31 class configitem(object):
32 32 """represent a known config item
33 33
34 34 :section: the official config section where to find this item,
35 35 :name: the official name within the section,
36 36 :default: default value for this item,
37 37 :alias: optional list of tuples as alternatives,
38 38 :generic: this is a generic definition, match name using regular expression.
39 39 """
40 40
41 41 def __init__(self, section, name, default=None, alias=(),
42 42 generic=False, priority=0):
43 43 self.section = section
44 44 self.name = name
45 45 self.default = default
46 46 self.alias = list(alias)
47 47 self.generic = generic
48 48 self.priority = priority
49 49 self._re = None
50 50 if generic:
51 51 self._re = re.compile(self.name)
52 52
53 53 class itemregister(dict):
54 54 """A specialized dictionary that can handle wild-card selection"""
55 55
56 56 def __init__(self):
57 57 super(itemregister, self).__init__()
58 58 self._generics = set()
59 59
60 60 def update(self, other):
61 61 super(itemregister, self).update(other)
62 62 self._generics.update(other._generics)
63 63
64 64 def __setitem__(self, key, item):
65 65 super(itemregister, self).__setitem__(key, item)
66 66 if item.generic:
67 67 self._generics.add(item)
68 68
69 69 def get(self, key):
70 70 if key in self:
71 71 return self[key]
72 72
73 73 # search for a matching generic item
74 74 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
75 75 for item in generics:
76 76 if item._re.match(key):
77 77 return item
78 78
79 79 # fallback to dict get
80 80 return super(itemregister, self).get(key)
81 81
82 82 coreitems = {}
83 83
84 84 def _register(configtable, *args, **kwargs):
85 85 item = configitem(*args, **kwargs)
86 86 section = configtable.setdefault(item.section, itemregister())
87 87 if item.name in section:
88 88 msg = "duplicated config item registration for '%s.%s'"
89 89 raise error.ProgrammingError(msg % (item.section, item.name))
90 90 section[item.name] = item
91 91
92 92 # special value for case where the default is derived from other values
93 93 dynamicdefault = object()
94 94
95 95 # Registering actual config items
96 96
97 97 def getitemregister(configtable):
98 98 return functools.partial(_register, configtable)
99 99
100 100 coreconfigitem = getitemregister(coreitems)
101 101
102 102 coreconfigitem('alias', '.*',
103 103 default=None,
104 104 generic=True,
105 105 )
106 106 coreconfigitem('annotate', 'nodates',
107 107 default=False,
108 108 )
109 109 coreconfigitem('annotate', 'showfunc',
110 110 default=False,
111 111 )
112 112 coreconfigitem('annotate', 'unified',
113 113 default=None,
114 114 )
115 115 coreconfigitem('annotate', 'git',
116 116 default=False,
117 117 )
118 118 coreconfigitem('annotate', 'ignorews',
119 119 default=False,
120 120 )
121 121 coreconfigitem('annotate', 'ignorewsamount',
122 122 default=False,
123 123 )
124 124 coreconfigitem('annotate', 'ignoreblanklines',
125 125 default=False,
126 126 )
127 127 coreconfigitem('annotate', 'ignorewseol',
128 128 default=False,
129 129 )
130 130 coreconfigitem('annotate', 'nobinary',
131 131 default=False,
132 132 )
133 133 coreconfigitem('annotate', 'noprefix',
134 134 default=False,
135 135 )
136 136 coreconfigitem('auth', 'cookiefile',
137 137 default=None,
138 138 )
139 139 # bookmarks.pushing: internal hack for discovery
140 140 coreconfigitem('bookmarks', 'pushing',
141 141 default=list,
142 142 )
143 143 # bundle.mainreporoot: internal hack for bundlerepo
144 144 coreconfigitem('bundle', 'mainreporoot',
145 145 default='',
146 146 )
147 147 # bundle.reorder: experimental config
148 148 coreconfigitem('bundle', 'reorder',
149 149 default='auto',
150 150 )
151 151 coreconfigitem('censor', 'policy',
152 152 default='abort',
153 153 )
154 154 coreconfigitem('chgserver', 'idletimeout',
155 155 default=3600,
156 156 )
157 157 coreconfigitem('chgserver', 'skiphash',
158 158 default=False,
159 159 )
160 160 coreconfigitem('cmdserver', 'log',
161 161 default=None,
162 162 )
163 163 coreconfigitem('color', '.*',
164 164 default=None,
165 165 generic=True,
166 166 )
167 167 coreconfigitem('color', 'mode',
168 168 default='auto',
169 169 )
170 170 coreconfigitem('color', 'pagermode',
171 171 default=dynamicdefault,
172 172 )
173 173 coreconfigitem('commands', 'status.relative',
174 174 default=False,
175 175 )
176 176 coreconfigitem('commands', 'status.skipstates',
177 177 default=[],
178 178 )
179 179 coreconfigitem('commands', 'status.verbose',
180 180 default=False,
181 181 )
182 182 coreconfigitem('commands', 'update.check',
183 183 default=None,
184 184 # Deprecated, remove after 4.4 release
185 185 alias=[('experimental', 'updatecheck')]
186 186 )
187 187 coreconfigitem('commands', 'update.requiredest',
188 188 default=False,
189 189 )
190 190 coreconfigitem('committemplate', '.*',
191 191 default=None,
192 192 generic=True,
193 193 )
194 194 coreconfigitem('debug', 'dirstate.delaywrite',
195 195 default=0,
196 196 )
197 197 coreconfigitem('defaults', '.*',
198 198 default=None,
199 199 generic=True,
200 200 )
201 201 coreconfigitem('devel', 'all-warnings',
202 202 default=False,
203 203 )
204 204 coreconfigitem('devel', 'bundle2.debug',
205 205 default=False,
206 206 )
207 207 coreconfigitem('devel', 'cache-vfs',
208 208 default=None,
209 209 )
210 210 coreconfigitem('devel', 'check-locks',
211 211 default=False,
212 212 )
213 213 coreconfigitem('devel', 'check-relroot',
214 214 default=False,
215 215 )
216 216 coreconfigitem('devel', 'default-date',
217 217 default=None,
218 218 )
219 219 coreconfigitem('devel', 'deprec-warn',
220 220 default=False,
221 221 )
222 222 coreconfigitem('devel', 'disableloaddefaultcerts',
223 223 default=False,
224 224 )
225 225 coreconfigitem('devel', 'warn-empty-changegroup',
226 226 default=False,
227 227 )
228 228 coreconfigitem('devel', 'legacy.exchange',
229 229 default=list,
230 230 )
231 231 coreconfigitem('devel', 'servercafile',
232 232 default='',
233 233 )
234 234 coreconfigitem('devel', 'serverexactprotocol',
235 235 default='',
236 236 )
237 237 coreconfigitem('devel', 'serverrequirecert',
238 238 default=False,
239 239 )
240 240 coreconfigitem('devel', 'strip-obsmarkers',
241 241 default=True,
242 242 )
243 243 coreconfigitem('devel', 'warn-config',
244 244 default=None,
245 245 )
246 246 coreconfigitem('devel', 'warn-config-default',
247 247 default=None,
248 248 )
249 249 coreconfigitem('devel', 'user.obsmarker',
250 250 default=None,
251 251 )
252 252 coreconfigitem('diff', 'nodates',
253 253 default=False,
254 254 )
255 255 coreconfigitem('diff', 'showfunc',
256 256 default=False,
257 257 )
258 258 coreconfigitem('diff', 'unified',
259 259 default=None,
260 260 )
261 261 coreconfigitem('diff', 'git',
262 262 default=False,
263 263 )
264 264 coreconfigitem('diff', 'ignorews',
265 265 default=False,
266 266 )
267 267 coreconfigitem('diff', 'ignorewsamount',
268 268 default=False,
269 269 )
270 270 coreconfigitem('diff', 'ignoreblanklines',
271 271 default=False,
272 272 )
273 273 coreconfigitem('diff', 'ignorewseol',
274 274 default=False,
275 275 )
276 276 coreconfigitem('diff', 'nobinary',
277 277 default=False,
278 278 )
279 279 coreconfigitem('diff', 'noprefix',
280 280 default=False,
281 281 )
282 282 coreconfigitem('email', 'bcc',
283 283 default=None,
284 284 )
285 285 coreconfigitem('email', 'cc',
286 286 default=None,
287 287 )
288 288 coreconfigitem('email', 'charsets',
289 289 default=list,
290 290 )
291 291 coreconfigitem('email', 'from',
292 292 default=None,
293 293 )
294 294 coreconfigitem('email', 'method',
295 295 default='smtp',
296 296 )
297 297 coreconfigitem('email', 'reply-to',
298 298 default=None,
299 299 )
300 300 coreconfigitem('experimental', 'allowdivergence',
301 301 default=False,
302 302 )
303 303 coreconfigitem('experimental', 'archivemetatemplate',
304 304 default=dynamicdefault,
305 305 )
306 306 coreconfigitem('experimental', 'bundle-phases',
307 307 default=False,
308 308 )
309 309 coreconfigitem('experimental', 'bundle2-advertise',
310 310 default=True,
311 311 )
312 312 coreconfigitem('experimental', 'bundle2-output-capture',
313 313 default=False,
314 314 )
315 315 coreconfigitem('experimental', 'bundle2.pushback',
316 316 default=False,
317 317 )
318 318 coreconfigitem('experimental', 'bundle2lazylocking',
319 319 default=False,
320 320 )
321 321 coreconfigitem('experimental', 'bundlecomplevel',
322 322 default=None,
323 323 )
324 324 coreconfigitem('experimental', 'changegroup3',
325 325 default=False,
326 326 )
327 327 coreconfigitem('experimental', 'clientcompressionengines',
328 328 default=list,
329 329 )
330 330 coreconfigitem('experimental', 'copytrace',
331 331 default='on',
332 332 )
333 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
334 default=100,
335 )
333 336 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
334 337 default=100,
335 338 )
336 339 coreconfigitem('experimental', 'crecordtest',
337 340 default=None,
338 341 )
339 342 coreconfigitem('experimental', 'editortmpinhg',
340 343 default=False,
341 344 )
342 345 coreconfigitem('experimental', 'maxdeltachainspan',
343 346 default=-1,
344 347 )
345 348 coreconfigitem('experimental', 'mmapindexthreshold',
346 349 default=None,
347 350 )
348 351 coreconfigitem('experimental', 'nonnormalparanoidcheck',
349 352 default=False,
350 353 )
351 354 coreconfigitem('experimental', 'effect-flags',
352 355 default=False,
353 356 )
354 357 coreconfigitem('experimental', 'stabilization',
355 358 default=list,
356 359 alias=[('experimental', 'evolution')],
357 360 )
358 361 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
359 362 default=False,
360 363 alias=[('experimental', 'evolution.bundle-obsmarker')],
361 364 )
362 365 coreconfigitem('experimental', 'stabilization.track-operation',
363 366 default=True,
364 367 alias=[('experimental', 'evolution.track-operation')]
365 368 )
366 369 coreconfigitem('experimental', 'exportableenviron',
367 370 default=list,
368 371 )
369 372 coreconfigitem('experimental', 'extendedheader.index',
370 373 default=None,
371 374 )
372 375 coreconfigitem('experimental', 'extendedheader.similarity',
373 376 default=False,
374 377 )
375 378 coreconfigitem('experimental', 'format.compression',
376 379 default='zlib',
377 380 )
378 381 coreconfigitem('experimental', 'graphshorten',
379 382 default=False,
380 383 )
381 384 coreconfigitem('experimental', 'graphstyle.parent',
382 385 default=dynamicdefault,
383 386 )
384 387 coreconfigitem('experimental', 'graphstyle.missing',
385 388 default=dynamicdefault,
386 389 )
387 390 coreconfigitem('experimental', 'graphstyle.grandparent',
388 391 default=dynamicdefault,
389 392 )
390 393 coreconfigitem('experimental', 'hook-track-tags',
391 394 default=False,
392 395 )
393 396 coreconfigitem('experimental', 'httppostargs',
394 397 default=False,
395 398 )
396 399 coreconfigitem('experimental', 'manifestv2',
397 400 default=False,
398 401 )
399 402 coreconfigitem('experimental', 'mergedriver',
400 403 default=None,
401 404 )
402 405 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
403 406 default=False,
404 407 )
405 408 coreconfigitem('experimental', 'rebase.multidest',
406 409 default=False,
407 410 )
408 411 coreconfigitem('experimental', 'revertalternateinteractivemode',
409 412 default=True,
410 413 )
411 414 coreconfigitem('experimental', 'revlogv2',
412 415 default=None,
413 416 )
414 417 coreconfigitem('experimental', 'spacemovesdown',
415 418 default=False,
416 419 )
417 420 coreconfigitem('experimental', 'sparse-read',
418 421 default=False,
419 422 )
420 423 coreconfigitem('experimental', 'sparse-read.density-threshold',
421 424 default=0.25,
422 425 )
423 426 coreconfigitem('experimental', 'sparse-read.min-block-size',
424 427 default='256K',
425 428 )
426 429 coreconfigitem('experimental', 'treemanifest',
427 430 default=False,
428 431 )
429 432 coreconfigitem('extensions', '.*',
430 433 default=None,
431 434 generic=True,
432 435 )
433 436 coreconfigitem('extdata', '.*',
434 437 default=None,
435 438 generic=True,
436 439 )
437 440 coreconfigitem('format', 'aggressivemergedeltas',
438 441 default=False,
439 442 )
440 443 coreconfigitem('format', 'chunkcachesize',
441 444 default=None,
442 445 )
443 446 coreconfigitem('format', 'dotencode',
444 447 default=True,
445 448 )
446 449 coreconfigitem('format', 'generaldelta',
447 450 default=False,
448 451 )
449 452 coreconfigitem('format', 'manifestcachesize',
450 453 default=None,
451 454 )
452 455 coreconfigitem('format', 'maxchainlen',
453 456 default=None,
454 457 )
455 458 coreconfigitem('format', 'obsstore-version',
456 459 default=None,
457 460 )
458 461 coreconfigitem('format', 'usefncache',
459 462 default=True,
460 463 )
461 464 coreconfigitem('format', 'usegeneraldelta',
462 465 default=True,
463 466 )
464 467 coreconfigitem('format', 'usestore',
465 468 default=True,
466 469 )
467 470 coreconfigitem('hooks', '.*',
468 471 default=dynamicdefault,
469 472 generic=True,
470 473 )
471 474 coreconfigitem('hgweb-paths', '.*',
472 475 default=list,
473 476 generic=True,
474 477 )
475 478 coreconfigitem('hostfingerprints', '.*',
476 479 default=list,
477 480 generic=True,
478 481 )
479 482 coreconfigitem('hostsecurity', 'ciphers',
480 483 default=None,
481 484 )
482 485 coreconfigitem('hostsecurity', 'disabletls10warning',
483 486 default=False,
484 487 )
485 488 coreconfigitem('hostsecurity', 'minimumprotocol',
486 489 default=dynamicdefault,
487 490 )
488 491 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
489 492 default=dynamicdefault,
490 493 generic=True,
491 494 )
492 495 coreconfigitem('hostsecurity', '.*:ciphers$',
493 496 default=dynamicdefault,
494 497 generic=True,
495 498 )
496 499 coreconfigitem('hostsecurity', '.*:fingerprints$',
497 500 default=list,
498 501 generic=True,
499 502 )
500 503 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
501 504 default=None,
502 505 generic=True,
503 506 )
504 507
505 508 coreconfigitem('http_proxy', 'always',
506 509 default=False,
507 510 )
508 511 coreconfigitem('http_proxy', 'host',
509 512 default=None,
510 513 )
511 514 coreconfigitem('http_proxy', 'no',
512 515 default=list,
513 516 )
514 517 coreconfigitem('http_proxy', 'passwd',
515 518 default=None,
516 519 )
517 520 coreconfigitem('http_proxy', 'user',
518 521 default=None,
519 522 )
520 523 coreconfigitem('logtoprocess', 'commandexception',
521 524 default=None,
522 525 )
523 526 coreconfigitem('logtoprocess', 'commandfinish',
524 527 default=None,
525 528 )
526 529 coreconfigitem('logtoprocess', 'command',
527 530 default=None,
528 531 )
529 532 coreconfigitem('logtoprocess', 'develwarn',
530 533 default=None,
531 534 )
532 535 coreconfigitem('logtoprocess', 'uiblocked',
533 536 default=None,
534 537 )
535 538 coreconfigitem('merge', 'checkunknown',
536 539 default='abort',
537 540 )
538 541 coreconfigitem('merge', 'checkignored',
539 542 default='abort',
540 543 )
541 544 coreconfigitem('merge', 'followcopies',
542 545 default=True,
543 546 )
544 547 coreconfigitem('merge', 'on-failure',
545 548 default='continue',
546 549 )
547 550 coreconfigitem('merge', 'preferancestor',
548 551 default=lambda: ['*'],
549 552 )
550 553 coreconfigitem('merge-tools', '.*',
551 554 default=None,
552 555 generic=True,
553 556 )
554 557 coreconfigitem('merge-tools', r'.*\.args$',
555 558 default="$local $base $other",
556 559 generic=True,
557 560 priority=-1,
558 561 )
559 562 coreconfigitem('merge-tools', r'.*\.binary$',
560 563 default=False,
561 564 generic=True,
562 565 priority=-1,
563 566 )
564 567 coreconfigitem('merge-tools', r'.*\.check$',
565 568 default=list,
566 569 generic=True,
567 570 priority=-1,
568 571 )
569 572 coreconfigitem('merge-tools', r'.*\.checkchanged$',
570 573 default=False,
571 574 generic=True,
572 575 priority=-1,
573 576 )
574 577 coreconfigitem('merge-tools', r'.*\.executable$',
575 578 default=dynamicdefault,
576 579 generic=True,
577 580 priority=-1,
578 581 )
579 582 coreconfigitem('merge-tools', r'.*\.fixeol$',
580 583 default=False,
581 584 generic=True,
582 585 priority=-1,
583 586 )
584 587 coreconfigitem('merge-tools', r'.*\.gui$',
585 588 default=False,
586 589 generic=True,
587 590 priority=-1,
588 591 )
589 592 coreconfigitem('merge-tools', r'.*\.priority$',
590 593 default=0,
591 594 generic=True,
592 595 priority=-1,
593 596 )
594 597 coreconfigitem('merge-tools', r'.*\.premerge$',
595 598 default=dynamicdefault,
596 599 generic=True,
597 600 priority=-1,
598 601 )
599 602 coreconfigitem('merge-tools', r'.*\.symlink$',
600 603 default=False,
601 604 generic=True,
602 605 priority=-1,
603 606 )
604 607 coreconfigitem('pager', 'attend-.*',
605 608 default=dynamicdefault,
606 609 generic=True,
607 610 )
608 611 coreconfigitem('pager', 'ignore',
609 612 default=list,
610 613 )
611 614 coreconfigitem('pager', 'pager',
612 615 default=dynamicdefault,
613 616 )
614 617 coreconfigitem('patch', 'eol',
615 618 default='strict',
616 619 )
617 620 coreconfigitem('patch', 'fuzz',
618 621 default=2,
619 622 )
620 623 coreconfigitem('paths', 'default',
621 624 default=None,
622 625 )
623 626 coreconfigitem('paths', 'default-push',
624 627 default=None,
625 628 )
626 629 coreconfigitem('paths', '.*',
627 630 default=None,
628 631 generic=True,
629 632 )
630 633 coreconfigitem('phases', 'checksubrepos',
631 634 default='follow',
632 635 )
633 636 coreconfigitem('phases', 'new-commit',
634 637 default='draft',
635 638 )
636 639 coreconfigitem('phases', 'publish',
637 640 default=True,
638 641 )
639 642 coreconfigitem('profiling', 'enabled',
640 643 default=False,
641 644 )
642 645 coreconfigitem('profiling', 'format',
643 646 default='text',
644 647 )
645 648 coreconfigitem('profiling', 'freq',
646 649 default=1000,
647 650 )
648 651 coreconfigitem('profiling', 'limit',
649 652 default=30,
650 653 )
651 654 coreconfigitem('profiling', 'nested',
652 655 default=0,
653 656 )
654 657 coreconfigitem('profiling', 'output',
655 658 default=None,
656 659 )
657 660 coreconfigitem('profiling', 'showmax',
658 661 default=0.999,
659 662 )
660 663 coreconfigitem('profiling', 'showmin',
661 664 default=dynamicdefault,
662 665 )
663 666 coreconfigitem('profiling', 'sort',
664 667 default='inlinetime',
665 668 )
666 669 coreconfigitem('profiling', 'statformat',
667 670 default='hotpath',
668 671 )
669 672 coreconfigitem('profiling', 'type',
670 673 default='stat',
671 674 )
672 675 coreconfigitem('progress', 'assume-tty',
673 676 default=False,
674 677 )
675 678 coreconfigitem('progress', 'changedelay',
676 679 default=1,
677 680 )
678 681 coreconfigitem('progress', 'clear-complete',
679 682 default=True,
680 683 )
681 684 coreconfigitem('progress', 'debug',
682 685 default=False,
683 686 )
684 687 coreconfigitem('progress', 'delay',
685 688 default=3,
686 689 )
687 690 coreconfigitem('progress', 'disable',
688 691 default=False,
689 692 )
690 693 coreconfigitem('progress', 'estimateinterval',
691 694 default=60.0,
692 695 )
693 696 coreconfigitem('progress', 'format',
694 697 default=lambda: ['topic', 'bar', 'number', 'estimate'],
695 698 )
696 699 coreconfigitem('progress', 'refresh',
697 700 default=0.1,
698 701 )
699 702 coreconfigitem('progress', 'width',
700 703 default=dynamicdefault,
701 704 )
702 705 coreconfigitem('push', 'pushvars.server',
703 706 default=False,
704 707 )
705 708 coreconfigitem('server', 'bundle1',
706 709 default=True,
707 710 )
708 711 coreconfigitem('server', 'bundle1gd',
709 712 default=None,
710 713 )
711 714 coreconfigitem('server', 'bundle1.pull',
712 715 default=None,
713 716 )
714 717 coreconfigitem('server', 'bundle1gd.pull',
715 718 default=None,
716 719 )
717 720 coreconfigitem('server', 'bundle1.push',
718 721 default=None,
719 722 )
720 723 coreconfigitem('server', 'bundle1gd.push',
721 724 default=None,
722 725 )
723 726 coreconfigitem('server', 'compressionengines',
724 727 default=list,
725 728 )
726 729 coreconfigitem('server', 'concurrent-push-mode',
727 730 default='strict',
728 731 )
729 732 coreconfigitem('server', 'disablefullbundle',
730 733 default=False,
731 734 )
732 735 coreconfigitem('server', 'maxhttpheaderlen',
733 736 default=1024,
734 737 )
735 738 coreconfigitem('server', 'preferuncompressed',
736 739 default=False,
737 740 )
738 741 coreconfigitem('server', 'uncompressed',
739 742 default=True,
740 743 )
741 744 coreconfigitem('server', 'uncompressedallowsecret',
742 745 default=False,
743 746 )
744 747 coreconfigitem('server', 'validate',
745 748 default=False,
746 749 )
747 750 coreconfigitem('server', 'zliblevel',
748 751 default=-1,
749 752 )
750 753 coreconfigitem('smtp', 'host',
751 754 default=None,
752 755 )
753 756 coreconfigitem('smtp', 'local_hostname',
754 757 default=None,
755 758 )
756 759 coreconfigitem('smtp', 'password',
757 760 default=None,
758 761 )
759 762 coreconfigitem('smtp', 'port',
760 763 default=dynamicdefault,
761 764 )
762 765 coreconfigitem('smtp', 'tls',
763 766 default='none',
764 767 )
765 768 coreconfigitem('smtp', 'username',
766 769 default=None,
767 770 )
768 771 coreconfigitem('sparse', 'missingwarning',
769 772 default=True,
770 773 )
771 774 coreconfigitem('templates', '.*',
772 775 default=None,
773 776 generic=True,
774 777 )
775 778 coreconfigitem('trusted', 'groups',
776 779 default=list,
777 780 )
778 781 coreconfigitem('trusted', 'users',
779 782 default=list,
780 783 )
781 784 coreconfigitem('ui', '_usedassubrepo',
782 785 default=False,
783 786 )
784 787 coreconfigitem('ui', 'allowemptycommit',
785 788 default=False,
786 789 )
787 790 coreconfigitem('ui', 'archivemeta',
788 791 default=True,
789 792 )
790 793 coreconfigitem('ui', 'askusername',
791 794 default=False,
792 795 )
793 796 coreconfigitem('ui', 'clonebundlefallback',
794 797 default=False,
795 798 )
796 799 coreconfigitem('ui', 'clonebundleprefers',
797 800 default=list,
798 801 )
799 802 coreconfigitem('ui', 'clonebundles',
800 803 default=True,
801 804 )
802 805 coreconfigitem('ui', 'color',
803 806 default='auto',
804 807 )
805 808 coreconfigitem('ui', 'commitsubrepos',
806 809 default=False,
807 810 )
808 811 coreconfigitem('ui', 'debug',
809 812 default=False,
810 813 )
811 814 coreconfigitem('ui', 'debugger',
812 815 default=None,
813 816 )
814 817 coreconfigitem('ui', 'fallbackencoding',
815 818 default=None,
816 819 )
817 820 coreconfigitem('ui', 'forcecwd',
818 821 default=None,
819 822 )
820 823 coreconfigitem('ui', 'forcemerge',
821 824 default=None,
822 825 )
823 826 coreconfigitem('ui', 'formatdebug',
824 827 default=False,
825 828 )
826 829 coreconfigitem('ui', 'formatjson',
827 830 default=False,
828 831 )
829 832 coreconfigitem('ui', 'formatted',
830 833 default=None,
831 834 )
832 835 coreconfigitem('ui', 'graphnodetemplate',
833 836 default=None,
834 837 )
835 838 coreconfigitem('ui', 'http2debuglevel',
836 839 default=None,
837 840 )
838 841 coreconfigitem('ui', 'interactive',
839 842 default=None,
840 843 )
841 844 coreconfigitem('ui', 'interface',
842 845 default=None,
843 846 )
844 847 coreconfigitem('ui', 'interface.chunkselector',
845 848 default=None,
846 849 )
847 850 coreconfigitem('ui', 'logblockedtimes',
848 851 default=False,
849 852 )
850 853 coreconfigitem('ui', 'logtemplate',
851 854 default=None,
852 855 )
853 856 coreconfigitem('ui', 'merge',
854 857 default=None,
855 858 )
856 859 coreconfigitem('ui', 'mergemarkers',
857 860 default='basic',
858 861 )
859 862 coreconfigitem('ui', 'mergemarkertemplate',
860 863 default=('{node|short} '
861 864 '{ifeq(tags, "tip", "", '
862 865 'ifeq(tags, "", "", "{tags} "))}'
863 866 '{if(bookmarks, "{bookmarks} ")}'
864 867 '{ifeq(branch, "default", "", "{branch} ")}'
865 868 '- {author|user}: {desc|firstline}')
866 869 )
867 870 coreconfigitem('ui', 'nontty',
868 871 default=False,
869 872 )
870 873 coreconfigitem('ui', 'origbackuppath',
871 874 default=None,
872 875 )
873 876 coreconfigitem('ui', 'paginate',
874 877 default=True,
875 878 )
876 879 coreconfigitem('ui', 'patch',
877 880 default=None,
878 881 )
879 882 coreconfigitem('ui', 'portablefilenames',
880 883 default='warn',
881 884 )
882 885 coreconfigitem('ui', 'promptecho',
883 886 default=False,
884 887 )
885 888 coreconfigitem('ui', 'quiet',
886 889 default=False,
887 890 )
888 891 coreconfigitem('ui', 'quietbookmarkmove',
889 892 default=False,
890 893 )
891 894 coreconfigitem('ui', 'remotecmd',
892 895 default='hg',
893 896 )
894 897 coreconfigitem('ui', 'report_untrusted',
895 898 default=True,
896 899 )
897 900 coreconfigitem('ui', 'rollback',
898 901 default=True,
899 902 )
900 903 coreconfigitem('ui', 'slash',
901 904 default=False,
902 905 )
903 906 coreconfigitem('ui', 'ssh',
904 907 default='ssh',
905 908 )
906 909 coreconfigitem('ui', 'statuscopies',
907 910 default=False,
908 911 )
909 912 coreconfigitem('ui', 'strict',
910 913 default=False,
911 914 )
912 915 coreconfigitem('ui', 'style',
913 916 default='',
914 917 )
915 918 coreconfigitem('ui', 'supportcontact',
916 919 default=None,
917 920 )
918 921 coreconfigitem('ui', 'textwidth',
919 922 default=78,
920 923 )
921 924 coreconfigitem('ui', 'timeout',
922 925 default='600',
923 926 )
924 927 coreconfigitem('ui', 'traceback',
925 928 default=False,
926 929 )
927 930 coreconfigitem('ui', 'tweakdefaults',
928 931 default=False,
929 932 )
930 933 coreconfigitem('ui', 'usehttp2',
931 934 default=False,
932 935 )
933 936 coreconfigitem('ui', 'username',
934 937 alias=[('ui', 'user')]
935 938 )
936 939 coreconfigitem('ui', 'verbose',
937 940 default=False,
938 941 )
939 942 coreconfigitem('verify', 'skipflags',
940 943 default=None,
941 944 )
942 945 coreconfigitem('web', 'allowbz2',
943 946 default=False,
944 947 )
945 948 coreconfigitem('web', 'allowgz',
946 949 default=False,
947 950 )
948 951 coreconfigitem('web', 'allowpull',
949 952 default=True,
950 953 )
951 954 coreconfigitem('web', 'allow_push',
952 955 default=list,
953 956 )
954 957 coreconfigitem('web', 'allowzip',
955 958 default=False,
956 959 )
957 960 coreconfigitem('web', 'archivesubrepos',
958 961 default=False,
959 962 )
960 963 coreconfigitem('web', 'cache',
961 964 default=True,
962 965 )
963 966 coreconfigitem('web', 'contact',
964 967 default=None,
965 968 )
966 969 coreconfigitem('web', 'deny_push',
967 970 default=list,
968 971 )
969 972 coreconfigitem('web', 'guessmime',
970 973 default=False,
971 974 )
972 975 coreconfigitem('web', 'hidden',
973 976 default=False,
974 977 )
975 978 coreconfigitem('web', 'labels',
976 979 default=list,
977 980 )
978 981 coreconfigitem('web', 'logoimg',
979 982 default='hglogo.png',
980 983 )
981 984 coreconfigitem('web', 'logourl',
982 985 default='https://mercurial-scm.org/',
983 986 )
984 987 coreconfigitem('web', 'accesslog',
985 988 default='-',
986 989 )
987 990 coreconfigitem('web', 'address',
988 991 default='',
989 992 )
990 993 coreconfigitem('web', 'allow_archive',
991 994 default=list,
992 995 )
993 996 coreconfigitem('web', 'allow_read',
994 997 default=list,
995 998 )
996 999 coreconfigitem('web', 'baseurl',
997 1000 default=None,
998 1001 )
999 1002 coreconfigitem('web', 'cacerts',
1000 1003 default=None,
1001 1004 )
1002 1005 coreconfigitem('web', 'certificate',
1003 1006 default=None,
1004 1007 )
1005 1008 coreconfigitem('web', 'collapse',
1006 1009 default=False,
1007 1010 )
1008 1011 coreconfigitem('web', 'csp',
1009 1012 default=None,
1010 1013 )
1011 1014 coreconfigitem('web', 'deny_read',
1012 1015 default=list,
1013 1016 )
1014 1017 coreconfigitem('web', 'descend',
1015 1018 default=True,
1016 1019 )
1017 1020 coreconfigitem('web', 'description',
1018 1021 default="",
1019 1022 )
1020 1023 coreconfigitem('web', 'encoding',
1021 1024 default=lambda: encoding.encoding,
1022 1025 )
1023 1026 coreconfigitem('web', 'errorlog',
1024 1027 default='-',
1025 1028 )
1026 1029 coreconfigitem('web', 'ipv6',
1027 1030 default=False,
1028 1031 )
1029 1032 coreconfigitem('web', 'maxchanges',
1030 1033 default=10,
1031 1034 )
1032 1035 coreconfigitem('web', 'maxfiles',
1033 1036 default=10,
1034 1037 )
1035 1038 coreconfigitem('web', 'maxshortchanges',
1036 1039 default=60,
1037 1040 )
1038 1041 coreconfigitem('web', 'motd',
1039 1042 default='',
1040 1043 )
1041 1044 coreconfigitem('web', 'name',
1042 1045 default=dynamicdefault,
1043 1046 )
1044 1047 coreconfigitem('web', 'port',
1045 1048 default=8000,
1046 1049 )
1047 1050 coreconfigitem('web', 'prefix',
1048 1051 default='',
1049 1052 )
1050 1053 coreconfigitem('web', 'push_ssl',
1051 1054 default=True,
1052 1055 )
1053 1056 coreconfigitem('web', 'refreshinterval',
1054 1057 default=20,
1055 1058 )
1056 1059 coreconfigitem('web', 'staticurl',
1057 1060 default=None,
1058 1061 )
1059 1062 coreconfigitem('web', 'stripes',
1060 1063 default=1,
1061 1064 )
1062 1065 coreconfigitem('web', 'style',
1063 1066 default='paper',
1064 1067 )
1065 1068 coreconfigitem('web', 'templates',
1066 1069 default=None,
1067 1070 )
1068 1071 coreconfigitem('web', 'view',
1069 1072 default='served',
1070 1073 )
1071 1074 coreconfigitem('worker', 'backgroundclose',
1072 1075 default=dynamicdefault,
1073 1076 )
1074 1077 # Windows defaults to a limit of 512 open files. A buffer of 128
1075 1078 # should give us enough headway.
1076 1079 coreconfigitem('worker', 'backgroundclosemaxqueue',
1077 1080 default=384,
1078 1081 )
1079 1082 coreconfigitem('worker', 'backgroundcloseminfilecount',
1080 1083 default=2048,
1081 1084 )
1082 1085 coreconfigitem('worker', 'backgroundclosethreadcount',
1083 1086 default=4,
1084 1087 )
1085 1088 coreconfigitem('worker', 'numcpus',
1086 1089 default=None,
1087 1090 )
1088 1091
1089 1092 # Rebase related configuration moved to core because other extension are doing
1090 1093 # strange things. For example, shelve import the extensions to reuse some bit
1091 1094 # without formally loading it.
1092 1095 coreconfigitem('commands', 'rebase.requiredest',
1093 1096 default=False,
1094 1097 )
1095 1098 coreconfigitem('experimental', 'rebaseskipobsolete',
1096 1099 default=True,
1097 1100 )
1098 1101 coreconfigitem('rebase', 'singletransaction',
1099 1102 default=False,
1100 1103 )
@@ -1,866 +1,884 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 from .i18n import _
15
14 16 from . import (
15 17 match as matchmod,
16 18 node,
17 19 pathutil,
18 20 scmutil,
19 21 util,
20 22 )
21 23
22 24 def _findlimit(repo, a, b):
23 25 """
24 26 Find the last revision that needs to be checked to ensure that a full
25 27 transitive closure for file copies can be properly calculated.
26 28 Generally, this means finding the earliest revision number that's an
27 29 ancestor of a or b but not both, except when a or b is a direct descendent
28 30 of the other, in which case we can return the minimum revnum of a and b.
29 31 None if no such revision exists.
30 32 """
31 33
32 34 # basic idea:
33 35 # - mark a and b with different sides
34 36 # - if a parent's children are all on the same side, the parent is
35 37 # on that side, otherwise it is on no side
36 38 # - walk the graph in topological order with the help of a heap;
37 39 # - add unseen parents to side map
38 40 # - clear side of any parent that has children on different sides
39 41 # - track number of interesting revs that might still be on a side
40 42 # - track the lowest interesting rev seen
41 43 # - quit when interesting revs is zero
42 44
43 45 cl = repo.changelog
44 46 working = len(cl) # pseudo rev for the working directory
45 47 if a is None:
46 48 a = working
47 49 if b is None:
48 50 b = working
49 51
50 52 side = {a: -1, b: 1}
51 53 visit = [-a, -b]
52 54 heapq.heapify(visit)
53 55 interesting = len(visit)
54 56 hascommonancestor = False
55 57 limit = working
56 58
57 59 while interesting:
58 60 r = -heapq.heappop(visit)
59 61 if r == working:
60 62 parents = [cl.rev(p) for p in repo.dirstate.parents()]
61 63 else:
62 64 parents = cl.parentrevs(r)
63 65 for p in parents:
64 66 if p < 0:
65 67 continue
66 68 if p not in side:
67 69 # first time we see p; add it to visit
68 70 side[p] = side[r]
69 71 if side[p]:
70 72 interesting += 1
71 73 heapq.heappush(visit, -p)
72 74 elif side[p] and side[p] != side[r]:
73 75 # p was interesting but now we know better
74 76 side[p] = 0
75 77 interesting -= 1
76 78 hascommonancestor = True
77 79 if side[r]:
78 80 limit = r # lowest rev visited
79 81 interesting -= 1
80 82
81 83 if not hascommonancestor:
82 84 return None
83 85
84 86 # Consider the following flow (see test-commit-amend.t under issue4405):
85 87 # 1/ File 'a0' committed
86 88 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
87 89 # 3/ Move back to first commit
88 90 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
89 91 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
90 92 #
91 93 # During the amend in step five, we will be in this state:
92 94 #
93 95 # @ 3 temporary amend commit for a1-amend
94 96 # |
95 97 # o 2 a1-amend
96 98 # |
97 99 # | o 1 a1
98 100 # |/
99 101 # o 0 a0
100 102 #
101 103 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
102 104 # yet the filelog has the copy information in rev 1 and we will not look
103 105 # back far enough unless we also look at the a and b as candidates.
104 106 # This only occurs when a is a descendent of b or visa-versa.
105 107 return min(limit, a, b)
106 108
107 109 def _chain(src, dst, a, b):
108 110 '''chain two sets of copies a->b'''
109 111 t = a.copy()
110 112 for k, v in b.iteritems():
111 113 if v in t:
112 114 # found a chain
113 115 if t[v] != k:
114 116 # file wasn't renamed back to itself
115 117 t[k] = t[v]
116 118 if v not in dst:
117 119 # chain was a rename, not a copy
118 120 del t[v]
119 121 if v in src:
120 122 # file is a copy of an existing file
121 123 t[k] = v
122 124
123 125 # remove criss-crossed copies
124 126 for k, v in t.items():
125 127 if k in src and v in dst:
126 128 del t[k]
127 129
128 130 return t
129 131
130 132 def _tracefile(fctx, am, limit=-1):
131 133 '''return file context that is the ancestor of fctx present in ancestor
132 134 manifest am, stopping after the first ancestor lower than limit'''
133 135
134 136 for f in fctx.ancestors():
135 137 if am.get(f.path(), None) == f.filenode():
136 138 return f
137 139 if limit >= 0 and f.linkrev() < limit and f.rev() < limit:
138 140 return None
139 141
140 142 def _dirstatecopies(d):
141 143 ds = d._repo.dirstate
142 144 c = ds.copies().copy()
143 145 for k in list(c):
144 146 if ds[k] not in 'anm':
145 147 del c[k]
146 148 return c
147 149
148 150 def _computeforwardmissing(a, b, match=None):
149 151 """Computes which files are in b but not a.
150 152 This is its own function so extensions can easily wrap this call to see what
151 153 files _forwardcopies is about to process.
152 154 """
153 155 ma = a.manifest()
154 156 mb = b.manifest()
155 157 return mb.filesnotin(ma, match=match)
156 158
157 159 def _forwardcopies(a, b, match=None):
158 160 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
159 161
160 162 # check for working copy
161 163 w = None
162 164 if b.rev() is None:
163 165 w = b
164 166 b = w.p1()
165 167 if a == b:
166 168 # short-circuit to avoid issues with merge states
167 169 return _dirstatecopies(w)
168 170
169 171 # files might have to be traced back to the fctx parent of the last
170 172 # one-side-only changeset, but not further back than that
171 173 limit = _findlimit(a._repo, a.rev(), b.rev())
172 174 if limit is None:
173 175 limit = -1
174 176 am = a.manifest()
175 177
176 178 # find where new files came from
177 179 # we currently don't try to find where old files went, too expensive
178 180 # this means we can miss a case like 'hg rm b; hg cp a b'
179 181 cm = {}
180 182
181 183 # Computing the forward missing is quite expensive on large manifests, since
182 184 # it compares the entire manifests. We can optimize it in the common use
183 185 # case of computing what copies are in a commit versus its parent (like
184 186 # during a rebase or histedit). Note, we exclude merge commits from this
185 187 # optimization, since the ctx.files() for a merge commit is not correct for
186 188 # this comparison.
187 189 forwardmissingmatch = match
188 190 if b.p1() == a and b.p2().node() == node.nullid:
189 191 filesmatcher = scmutil.matchfiles(a._repo, b.files())
190 192 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
191 193 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
192 194
193 195 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
194 196 for f in missing:
195 197 fctx = b[f]
196 198 fctx._ancestrycontext = ancestrycontext
197 199 ofctx = _tracefile(fctx, am, limit)
198 200 if ofctx:
199 201 cm[f] = ofctx.path()
200 202
201 203 # combine copies from dirstate if necessary
202 204 if w is not None:
203 205 cm = _chain(a, w, cm, _dirstatecopies(w))
204 206
205 207 return cm
206 208
207 209 def _backwardrenames(a, b):
208 210 if a._repo.ui.config('experimental', 'copytrace') == 'off':
209 211 return {}
210 212
211 213 # Even though we're not taking copies into account, 1:n rename situations
212 214 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
213 215 # arbitrarily pick one of the renames.
214 216 f = _forwardcopies(b, a)
215 217 r = {}
216 218 for k, v in sorted(f.iteritems()):
217 219 # remove copies
218 220 if v in a:
219 221 continue
220 222 r[v] = k
221 223 return r
222 224
223 225 def pathcopies(x, y, match=None):
224 226 '''find {dst@y: src@x} copy mapping for directed compare'''
225 227 if x == y or not x or not y:
226 228 return {}
227 229 a = y.ancestor(x)
228 230 if a == x:
229 231 return _forwardcopies(x, y, match=match)
230 232 if a == y:
231 233 return _backwardrenames(x, y)
232 234 return _chain(x, y, _backwardrenames(x, a),
233 235 _forwardcopies(a, y, match=match))
234 236
235 237 def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
236 238 """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
237 239 and c2. This is its own function so extensions can easily wrap this call
238 240 to see what files mergecopies is about to process.
239 241
240 242 Even though c1 and c2 are not used in this function, they are useful in
241 243 other extensions for being able to read the file nodes of the changed files.
242 244
243 245 "baselabel" can be passed to help distinguish the multiple computations
244 246 done in the graft case.
245 247 """
246 248 u1 = sorted(addedinm1 - addedinm2)
247 249 u2 = sorted(addedinm2 - addedinm1)
248 250
249 251 header = " unmatched files in %s"
250 252 if baselabel:
251 253 header += ' (from %s)' % baselabel
252 254 if u1:
253 255 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1)))
254 256 if u2:
255 257 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2)))
256 258 return u1, u2
257 259
258 260 def _makegetfctx(ctx):
259 261 """return a 'getfctx' function suitable for _checkcopies usage
260 262
261 263 We have to re-setup the function building 'filectx' for each
262 264 '_checkcopies' to ensure the linkrev adjustment is properly setup for
263 265 each. Linkrev adjustment is important to avoid bug in rename
264 266 detection. Moreover, having a proper '_ancestrycontext' setup ensures
265 267 the performance impact of this adjustment is kept limited. Without it,
266 268 each file could do a full dag traversal making the time complexity of
267 269 the operation explode (see issue4537).
268 270
269 271 This function exists here mostly to limit the impact on stable. Feel
270 272 free to refactor on default.
271 273 """
272 274 rev = ctx.rev()
273 275 repo = ctx._repo
274 276 ac = getattr(ctx, '_ancestrycontext', None)
275 277 if ac is None:
276 278 revs = [rev]
277 279 if rev is None:
278 280 revs = [p.rev() for p in ctx.parents()]
279 281 ac = repo.changelog.ancestors(revs, inclusive=True)
280 282 ctx._ancestrycontext = ac
281 283 def makectx(f, n):
282 284 if n in node.wdirnodes: # in a working context?
283 285 if ctx.rev() is None:
284 286 return ctx.filectx(f)
285 287 return repo[None][f]
286 288 fctx = repo.filectx(f, fileid=n)
287 289 # setup only needed for filectx not create from a changectx
288 290 fctx._ancestrycontext = ac
289 291 fctx._descendantrev = rev
290 292 return fctx
291 293 return util.lrucachefunc(makectx)
292 294
293 295 def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge):
294 296 """combine partial copy paths"""
295 297 remainder = {}
296 298 for f in copyfrom:
297 299 if f in copyto:
298 300 finalcopy[copyto[f]] = copyfrom[f]
299 301 del copyto[f]
300 302 for f in incompletediverge:
301 303 assert f not in diverge
302 304 ic = incompletediverge[f]
303 305 if ic[0] in copyto:
304 306 diverge[f] = [copyto[ic[0]], ic[1]]
305 307 else:
306 308 remainder[f] = ic
307 309 return remainder
308 310
309 311 def mergecopies(repo, c1, c2, base):
310 312 """
311 313 The function calling different copytracing algorithms on the basis of config
312 314 which find moves and copies between context c1 and c2 that are relevant for
313 315 merging. 'base' will be used as the merge base.
314 316
315 317 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
316 318 files that were moved/ copied in one merge parent and modified in another.
317 319 For example:
318 320
319 321 o ---> 4 another commit
320 322 |
321 323 | o ---> 3 commit that modifies a.txt
322 324 | /
323 325 o / ---> 2 commit that moves a.txt to b.txt
324 326 |/
325 327 o ---> 1 merge base
326 328
327 329 If we try to rebase revision 3 on revision 4, since there is no a.txt in
328 330 revision 4, and if user have copytrace disabled, we prints the following
329 331 message:
330 332
331 333 ```other changed <file> which local deleted```
332 334
333 335 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
334 336 "dirmove".
335 337
336 338 "copy" is a mapping from destination name -> source name,
337 339 where source is in c1 and destination is in c2 or vice-versa.
338 340
339 341 "movewithdir" is a mapping from source name -> destination name,
340 342 where the file at source present in one context but not the other
341 343 needs to be moved to destination by the merge process, because the
342 344 other context moved the directory it is in.
343 345
344 346 "diverge" is a mapping of source name -> list of destination names
345 347 for divergent renames.
346 348
347 349 "renamedelete" is a mapping of source name -> list of destination
348 350 names for files deleted in c1 that were renamed in c2 or vice-versa.
349 351
350 352 "dirmove" is a mapping of detected source dir -> destination dir renames.
351 353 This is needed for handling changes to new files previously grafted into
352 354 renamed directories.
353 355 """
354 356 # avoid silly behavior for update from empty dir
355 357 if not c1 or not c2 or c1 == c2:
356 358 return {}, {}, {}, {}, {}
357 359
358 360 # avoid silly behavior for parent -> working dir
359 361 if c2.node() is None and c1.node() == repo.dirstate.p1():
360 362 return repo.dirstate.copies(), {}, {}, {}, {}
361 363
362 364 copytracing = repo.ui.config('experimental', 'copytrace')
363 365
364 366 # Copy trace disabling is explicitly below the node == p1 logic above
365 367 # because the logic above is required for a simple copy to be kept across a
366 368 # rebase.
367 369 if copytracing == 'off':
368 370 return {}, {}, {}, {}, {}
369 371 elif copytracing == 'heuristics':
370 372 # Do full copytracing if only non-public revisions are involved as
371 373 # that will be fast enough and will also cover the copies which could
372 374 # be missed by heuristics
373 375 if _isfullcopytraceable(repo, c1, base):
374 376 return _fullcopytracing(repo, c1, c2, base)
375 377 return _heuristicscopytracing(repo, c1, c2, base)
376 378 else:
377 379 return _fullcopytracing(repo, c1, c2, base)
378 380
379 381 def _isfullcopytraceable(repo, c1, base):
380 382 """ Checks that if base, source and destination are all no-public branches,
381 383 if yes let's use the full copytrace algorithm for increased capabilities
382 384 since it will be fast enough.
383 385
384 386 `experimental.copytrace.sourcecommitlimit` can be used to set a limit for
385 387 number of changesets from c1 to base such that if number of changesets are
386 388 more than the limit, full copytracing algorithm won't be used.
387 389 """
388 390 if c1.rev() is None:
389 391 c1 = c1.p1()
390 392 if c1.mutable() and base.mutable():
391 393 sourcecommitlimit = repo.ui.configint('experimental',
392 394 'copytrace.sourcecommitlimit')
393 395 commits = len(repo.revs('%d::%d', base.rev(), c1.rev()))
394 396 return commits < sourcecommitlimit
395 397 return False
396 398
397 399 def _fullcopytracing(repo, c1, c2, base):
398 400 """ The full copytracing algorithm which finds all the new files that were
399 401 added from merge base up to the top commit and for each file it checks if
400 402 this file was copied from another file.
401 403
402 404 This is pretty slow when a lot of changesets are involved but will track all
403 405 the copies.
404 406 """
405 407 # In certain scenarios (e.g. graft, update or rebase), base can be
406 408 # overridden We still need to know a real common ancestor in this case We
407 409 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
408 410 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
409 411 # caller may not know if the revision passed in lieu of the CA is a genuine
410 412 # common ancestor or not without explicitly checking it, it's better to
411 413 # determine that here.
412 414 #
413 415 # base.descendant(wc) and base.descendant(base) are False, work around that
414 416 _c1 = c1.p1() if c1.rev() is None else c1
415 417 _c2 = c2.p1() if c2.rev() is None else c2
416 418 # an endpoint is "dirty" if it isn't a descendant of the merge base
417 419 # if we have a dirty endpoint, we need to trigger graft logic, and also
418 420 # keep track of which endpoint is dirty
419 421 dirtyc1 = not (base == _c1 or base.descendant(_c1))
420 422 dirtyc2 = not (base == _c2 or base.descendant(_c2))
421 423 graft = dirtyc1 or dirtyc2
422 424 tca = base
423 425 if graft:
424 426 tca = _c1.ancestor(_c2)
425 427
426 428 limit = _findlimit(repo, c1.rev(), c2.rev())
427 429 if limit is None:
428 430 # no common ancestor, no copies
429 431 return {}, {}, {}, {}, {}
430 432 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
431 433
432 434 m1 = c1.manifest()
433 435 m2 = c2.manifest()
434 436 mb = base.manifest()
435 437
436 438 # gather data from _checkcopies:
437 439 # - diverge = record all diverges in this dict
438 440 # - copy = record all non-divergent copies in this dict
439 441 # - fullcopy = record all copies in this dict
440 442 # - incomplete = record non-divergent partial copies here
441 443 # - incompletediverge = record divergent partial copies here
442 444 diverge = {} # divergence data is shared
443 445 incompletediverge = {}
444 446 data1 = {'copy': {},
445 447 'fullcopy': {},
446 448 'incomplete': {},
447 449 'diverge': diverge,
448 450 'incompletediverge': incompletediverge,
449 451 }
450 452 data2 = {'copy': {},
451 453 'fullcopy': {},
452 454 'incomplete': {},
453 455 'diverge': diverge,
454 456 'incompletediverge': incompletediverge,
455 457 }
456 458
457 459 # find interesting file sets from manifests
458 460 addedinm1 = m1.filesnotin(mb)
459 461 addedinm2 = m2.filesnotin(mb)
460 462 bothnew = sorted(addedinm1 & addedinm2)
461 463 if tca == base:
462 464 # unmatched file from base
463 465 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
464 466 u1u, u2u = u1r, u2r
465 467 else:
466 468 # unmatched file from base (DAG rotation in the graft case)
467 469 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
468 470 baselabel='base')
469 471 # unmatched file from topological common ancestors (no DAG rotation)
470 472 # need to recompute this for directory move handling when grafting
471 473 mta = tca.manifest()
472 474 u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
473 475 m2.filesnotin(mta),
474 476 baselabel='topological common ancestor')
475 477
476 478 for f in u1u:
477 479 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)
478 480
479 481 for f in u2u:
480 482 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)
481 483
482 484 copy = dict(data1['copy'])
483 485 copy.update(data2['copy'])
484 486 fullcopy = dict(data1['fullcopy'])
485 487 fullcopy.update(data2['fullcopy'])
486 488
487 489 if dirtyc1:
488 490 _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge,
489 491 incompletediverge)
490 492 else:
491 493 _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge,
492 494 incompletediverge)
493 495
494 496 renamedelete = {}
495 497 renamedeleteset = set()
496 498 divergeset = set()
497 499 for of, fl in list(diverge.items()):
498 500 if len(fl) == 1 or of in c1 or of in c2:
499 501 del diverge[of] # not actually divergent, or not a rename
500 502 if of not in c1 and of not in c2:
501 503 # renamed on one side, deleted on the other side, but filter
502 504 # out files that have been renamed and then deleted
503 505 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
504 506 renamedeleteset.update(fl) # reverse map for below
505 507 else:
506 508 divergeset.update(fl) # reverse map for below
507 509
508 510 if bothnew:
509 511 repo.ui.debug(" unmatched files new in both:\n %s\n"
510 512 % "\n ".join(bothnew))
511 513 bothdiverge = {}
512 514 bothincompletediverge = {}
513 515 remainder = {}
514 516 both1 = {'copy': {},
515 517 'fullcopy': {},
516 518 'incomplete': {},
517 519 'diverge': bothdiverge,
518 520 'incompletediverge': bothincompletediverge
519 521 }
520 522 both2 = {'copy': {},
521 523 'fullcopy': {},
522 524 'incomplete': {},
523 525 'diverge': bothdiverge,
524 526 'incompletediverge': bothincompletediverge
525 527 }
526 528 for f in bothnew:
527 529 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)
528 530 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)
529 531 if dirtyc1:
530 532 # incomplete copies may only be found on the "dirty" side for bothnew
531 533 assert not both2['incomplete']
532 534 remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge,
533 535 bothincompletediverge)
534 536 elif dirtyc2:
535 537 assert not both1['incomplete']
536 538 remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge,
537 539 bothincompletediverge)
538 540 else:
539 541 # incomplete copies and divergences can't happen outside grafts
540 542 assert not both1['incomplete']
541 543 assert not both2['incomplete']
542 544 assert not bothincompletediverge
543 545 for f in remainder:
544 546 assert f not in bothdiverge
545 547 ic = remainder[f]
546 548 if ic[0] in (m1 if dirtyc1 else m2):
547 549 # backed-out rename on one side, but watch out for deleted files
548 550 bothdiverge[f] = ic
549 551 for of, fl in bothdiverge.items():
550 552 if len(fl) == 2 and fl[0] == fl[1]:
551 553 copy[fl[0]] = of # not actually divergent, just matching renames
552 554
553 555 if fullcopy and repo.ui.debugflag:
554 556 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
555 557 "% = renamed and deleted):\n")
556 558 for f in sorted(fullcopy):
557 559 note = ""
558 560 if f in copy:
559 561 note += "*"
560 562 if f in divergeset:
561 563 note += "!"
562 564 if f in renamedeleteset:
563 565 note += "%"
564 566 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
565 567 note))
566 568 del divergeset
567 569
568 570 if not fullcopy:
569 571 return copy, {}, diverge, renamedelete, {}
570 572
571 573 repo.ui.debug(" checking for directory renames\n")
572 574
573 575 # generate a directory move map
574 576 d1, d2 = c1.dirs(), c2.dirs()
575 577 # Hack for adding '', which is not otherwise added, to d1 and d2
576 578 d1.addpath('/')
577 579 d2.addpath('/')
578 580 invalid = set()
579 581 dirmove = {}
580 582
581 583 # examine each file copy for a potential directory move, which is
582 584 # when all the files in a directory are moved to a new directory
583 585 for dst, src in fullcopy.iteritems():
584 586 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
585 587 if dsrc in invalid:
586 588 # already seen to be uninteresting
587 589 continue
588 590 elif dsrc in d1 and ddst in d1:
589 591 # directory wasn't entirely moved locally
590 592 invalid.add(dsrc + "/")
591 593 elif dsrc in d2 and ddst in d2:
592 594 # directory wasn't entirely moved remotely
593 595 invalid.add(dsrc + "/")
594 596 elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/":
595 597 # files from the same directory moved to two different places
596 598 invalid.add(dsrc + "/")
597 599 else:
598 600 # looks good so far
599 601 dirmove[dsrc + "/"] = ddst + "/"
600 602
601 603 for i in invalid:
602 604 if i in dirmove:
603 605 del dirmove[i]
604 606 del d1, d2, invalid
605 607
606 608 if not dirmove:
607 609 return copy, {}, diverge, renamedelete, {}
608 610
609 611 for d in dirmove:
610 612 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
611 613 (d, dirmove[d]))
612 614
613 615 movewithdir = {}
614 616 # check unaccounted nonoverlapping files against directory moves
615 617 for f in u1r + u2r:
616 618 if f not in fullcopy:
617 619 for d in dirmove:
618 620 if f.startswith(d):
619 621 # new file added in a directory that was moved, move it
620 622 df = dirmove[d] + f[len(d):]
621 623 if df not in copy:
622 624 movewithdir[f] = df
623 625 repo.ui.debug((" pending file src: '%s' -> "
624 626 "dst: '%s'\n") % (f, df))
625 627 break
626 628
627 629 return copy, movewithdir, diverge, renamedelete, dirmove
628 630
629 631 def _heuristicscopytracing(repo, c1, c2, base):
630 632 """ Fast copytracing using filename heuristics
631 633
632 634 Assumes that moves or renames are of following two types:
633 635
634 636 1) Inside a directory only (same directory name but different filenames)
635 637 2) Move from one directory to another
636 638 (same filenames but different directory names)
637 639
638 640 Works only when there are no merge commits in the "source branch".
639 641 Source branch is commits from base up to c2 not including base.
640 642
641 643 If merge is involved it fallbacks to _fullcopytracing().
642 644
643 645 Can be used by setting the following config:
644 646
645 647 [experimental]
646 648 copytrace = heuristics
649
650 In some cases the copy/move candidates found by heuristics can be very large
651 in number and that will make the algorithm slow. The number of possible
652 candidates to check can be limited by using the config
653 `experimental.copytrace.movecandidateslimit` which defaults to 100.
647 654 """
648 655
649 656 if c1.rev() is None:
650 657 c1 = c1.p1()
651 658 if c2.rev() is None:
652 659 c2 = c2.p1()
653 660
654 661 copies = {}
655 662
656 663 changedfiles = set()
657 664 m1 = c1.manifest()
658 665 if not repo.revs('%d::%d', base.rev(), c2.rev()):
659 666 # If base is not in c2 branch, we switch to fullcopytracing
660 667 repo.ui.debug("switching to full copytracing as base is not "
661 668 "an ancestor of c2\n")
662 669 return _fullcopytracing(repo, c1, c2, base)
663 670
664 671 ctx = c2
665 672 while ctx != base:
666 673 if len(ctx.parents()) == 2:
667 674 # To keep things simple let's not handle merges
668 675 repo.ui.debug("switching to full copytracing because of merges\n")
669 676 return _fullcopytracing(repo, c1, c2, base)
670 677 changedfiles.update(ctx.files())
671 678 ctx = ctx.p1()
672 679
673 680 cp = _forwardcopies(base, c2)
674 681 for dst, src in cp.iteritems():
675 682 if src in m1:
676 683 copies[dst] = src
677 684
678 685 # file is missing if it isn't present in the destination, but is present in
679 686 # the base and present in the source.
680 687 # Presence in the base is important to exclude added files, presence in the
681 688 # source is important to exclude removed files.
682 689 missingfiles = filter(lambda f: f not in m1 and f in base and f in c2,
683 690 changedfiles)
684 691
685 692 if missingfiles:
686 693 basenametofilename = collections.defaultdict(list)
687 694 dirnametofilename = collections.defaultdict(list)
688 695
689 696 for f in m1.filesnotin(base.manifest()):
690 697 basename = os.path.basename(f)
691 698 dirname = os.path.dirname(f)
692 699 basenametofilename[basename].append(f)
693 700 dirnametofilename[dirname].append(f)
694 701
695 702 # in case of a rebase/graft, base may not be a common ancestor
696 703 anc = c1.ancestor(c2)
697 704
698 705 for f in missingfiles:
699 706 basename = os.path.basename(f)
700 707 dirname = os.path.dirname(f)
701 708 samebasename = basenametofilename[basename]
702 709 samedirname = dirnametofilename[dirname]
703 710 movecandidates = samebasename + samedirname
704 711 # f is guaranteed to be present in c2, that's why
705 712 # c2.filectx(f) won't fail
706 713 f2 = c2.filectx(f)
714 # we can have a lot of candidates which can slow down the heuristics
715 # config value to limit the number of candidates moves to check
716 maxcandidates = repo.ui.configint('experimental',
717 'copytrace.movecandidateslimit')
718
719 if len(movecandidates) > maxcandidates:
720 repo.ui.status(_("skipping copytracing for '%s', more "
721 "candidates than the limit: %d\n")
722 % (f, len(movecandidates)))
723 continue
724
707 725 for candidate in movecandidates:
708 726 f1 = c1.filectx(candidate)
709 727 if _related(f1, f2, anc.rev()):
710 728 # if there are a few related copies then we'll merge
711 729 # changes into all of them. This matches the behaviour
712 730 # of upstream copytracing
713 731 copies[candidate] = f
714 732
715 733 return copies, {}, {}, {}, {}
716 734
717 735 def _related(f1, f2, limit):
718 736 """return True if f1 and f2 filectx have a common ancestor
719 737
720 738 Walk back to common ancestor to see if the two files originate
721 739 from the same file. Since workingfilectx's rev() is None it messes
722 740 up the integer comparison logic, hence the pre-step check for
723 741 None (f1 and f2 can only be workingfilectx's initially).
724 742 """
725 743
726 744 if f1 == f2:
727 745 return f1 # a match
728 746
729 747 g1, g2 = f1.ancestors(), f2.ancestors()
730 748 try:
731 749 f1r, f2r = f1.linkrev(), f2.linkrev()
732 750
733 751 if f1r is None:
734 752 f1 = next(g1)
735 753 if f2r is None:
736 754 f2 = next(g2)
737 755
738 756 while True:
739 757 f1r, f2r = f1.linkrev(), f2.linkrev()
740 758 if f1r > f2r:
741 759 f1 = next(g1)
742 760 elif f2r > f1r:
743 761 f2 = next(g2)
744 762 elif f1 == f2:
745 763 return f1 # a match
746 764 elif f1r == f2r or f1r < limit or f2r < limit:
747 765 return False # copy no longer relevant
748 766 except StopIteration:
749 767 return False
750 768
751 769 def _checkcopies(srcctx, dstctx, f, base, tca, remotebase, limit, data):
752 770 """
753 771 check possible copies of f from msrc to mdst
754 772
755 773 srcctx = starting context for f in msrc
756 774 dstctx = destination context for f in mdst
757 775 f = the filename to check (as in msrc)
758 776 base = the changectx used as a merge base
759 777 tca = topological common ancestor for graft-like scenarios
760 778 remotebase = True if base is outside tca::srcctx, False otherwise
761 779 limit = the rev number to not search beyond
762 780 data = dictionary of dictionary to store copy data. (see mergecopies)
763 781
764 782 note: limit is only an optimization, and provides no guarantee that
765 783 irrelevant revisions will not be visited
766 784 there is no easy way to make this algorithm stop in a guaranteed way
767 785 once it "goes behind a certain revision".
768 786 """
769 787
770 788 msrc = srcctx.manifest()
771 789 mdst = dstctx.manifest()
772 790 mb = base.manifest()
773 791 mta = tca.manifest()
774 792 # Might be true if this call is about finding backward renames,
775 793 # This happens in the case of grafts because the DAG is then rotated.
776 794 # If the file exists in both the base and the source, we are not looking
777 795 # for a rename on the source side, but on the part of the DAG that is
778 796 # traversed backwards.
779 797 #
780 798 # In the case there is both backward and forward renames (before and after
781 799 # the base) this is more complicated as we must detect a divergence.
782 800 # We use 'backwards = False' in that case.
783 801 backwards = not remotebase and base != tca and f in mb
784 802 getsrcfctx = _makegetfctx(srcctx)
785 803 getdstfctx = _makegetfctx(dstctx)
786 804
787 805 if msrc[f] == mb.get(f) and not remotebase:
788 806 # Nothing to merge
789 807 return
790 808
791 809 of = None
792 810 seen = {f}
793 811 for oc in getsrcfctx(f, msrc[f]).ancestors():
794 812 ocr = oc.linkrev()
795 813 of = oc.path()
796 814 if of in seen:
797 815 # check limit late - grab last rename before
798 816 if ocr < limit:
799 817 break
800 818 continue
801 819 seen.add(of)
802 820
803 821 # remember for dir rename detection
804 822 if backwards:
805 823 data['fullcopy'][of] = f # grafting backwards through renames
806 824 else:
807 825 data['fullcopy'][f] = of
808 826 if of not in mdst:
809 827 continue # no match, keep looking
810 828 if mdst[of] == mb.get(of):
811 829 return # no merge needed, quit early
812 830 c2 = getdstfctx(of, mdst[of])
813 831 # c2 might be a plain new file on added on destination side that is
814 832 # unrelated to the droids we are looking for.
815 833 cr = _related(oc, c2, tca.rev())
816 834 if cr and (of == f or of == c2.path()): # non-divergent
817 835 if backwards:
818 836 data['copy'][of] = f
819 837 elif of in mb:
820 838 data['copy'][f] = of
821 839 elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename
822 840 data['copy'][of] = f
823 841 del data['fullcopy'][f]
824 842 data['fullcopy'][of] = f
825 843 else: # divergence w.r.t. graft CA on one side of topological CA
826 844 for sf in seen:
827 845 if sf in mb:
828 846 assert sf not in data['diverge']
829 847 data['diverge'][sf] = [f, of]
830 848 break
831 849 return
832 850
833 851 if of in mta:
834 852 if backwards or remotebase:
835 853 data['incomplete'][of] = f
836 854 else:
837 855 for sf in seen:
838 856 if sf in mb:
839 857 if tca == base:
840 858 data['diverge'].setdefault(sf, []).append(f)
841 859 else:
842 860 data['incompletediverge'][sf] = [of, f]
843 861 return
844 862
845 863 def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None):
846 864 '''reproduce copies from fromrev to rev in the dirstate
847 865
848 866 If skiprev is specified, it's a revision that should be used to
849 867 filter copy records. Any copies that occur between fromrev and
850 868 skiprev will not be duplicated, even if they appear in the set of
851 869 copies between fromrev and rev.
852 870 '''
853 871 exclude = {}
854 872 if (skiprev is not None and
855 873 repo.ui.config('experimental', 'copytrace') != 'off'):
856 874 # copytrace='off' skips this line, but not the entire function because
857 875 # the line below is O(size of the repo) during a rebase, while the rest
858 876 # of the function is much faster (and is required for carrying copy
859 877 # metadata across the rebase anyway).
860 878 exclude = pathcopies(repo[fromrev], repo[skiprev])
861 879 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
862 880 # copies.pathcopies returns backward renames, so dst might not
863 881 # actually be in the dirstate
864 882 if dst in exclude:
865 883 continue
866 884 wctx[dst].markcopied(src)
@@ -1,657 +1,716 b''
1 1 Test for the heuristic copytracing algorithm
2 2 ============================================
3 3
4 4 $ cat >> $TESTTMP/copytrace.sh << '__EOF__'
5 5 > initclient() {
6 6 > cat >> $1/.hg/hgrc <<EOF
7 7 > [experimental]
8 8 > copytrace = heuristics
9 9 > copytrace.sourcecommitlimit = -1
10 10 > EOF
11 11 > }
12 12 > __EOF__
13 13 $ . "$TESTTMP/copytrace.sh"
14 14
15 15 $ cat >> $HGRCPATH << EOF
16 16 > [extensions]
17 17 > rebase=
18 18 > shelve=
19 19 > EOF
20 20
21 21 NOTE: calling initclient() set copytrace.sourcecommitlimit=-1 as we want to
22 22 prevent the full copytrace algorithm to run and test the heuristic algorithm
23 23 without complexing the test cases with public and draft commits.
24 24
25 25 Check filename heuristics (same dirname and same basename)
26 26 ----------------------------------------------------------
27 27
28 28 $ hg init repo
29 29 $ initclient repo
30 30 $ cd repo
31 31 $ echo a > a
32 32 $ mkdir dir
33 33 $ echo a > dir/file.txt
34 34 $ hg addremove
35 35 adding a
36 36 adding dir/file.txt
37 37 $ hg ci -m initial
38 38 $ hg mv a b
39 39 $ hg mv -q dir dir2
40 40 $ hg ci -m 'mv a b, mv dir/ dir2/'
41 41 $ hg up -q 0
42 42 $ echo b > a
43 43 $ echo b > dir/file.txt
44 44 $ hg ci -qm 'mod a, mod dir/file.txt'
45 45
46 46 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
47 47 @ changeset: 557f403c0afd2a3cf15d7e2fb1f1001a8b85e081
48 48 | desc: mod a, mod dir/file.txt
49 49 | o changeset: 928d74bc9110681920854d845c06959f6dfc9547
50 50 |/ desc: mv a b, mv dir/ dir2/
51 51 o changeset: 3c482b16e54596fed340d05ffaf155f156cda7ee
52 52 desc: initial
53 53
54 54 $ hg rebase -s . -d 1
55 55 rebasing 2:557f403c0afd "mod a, mod dir/file.txt" (tip)
56 56 merging b and a to b
57 57 merging dir2/file.txt and dir/file.txt to dir2/file.txt
58 58 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/557f403c0afd-9926eeff-rebase.hg (glob)
59 59 $ cd ..
60 60 $ rm -rf repo
61 61
62 62 Make sure filename heuristics do not when they are not related
63 63 --------------------------------------------------------------
64 64
65 65 $ hg init repo
66 66 $ initclient repo
67 67 $ cd repo
68 68 $ echo 'somecontent' > a
69 69 $ hg add a
70 70 $ hg ci -m initial
71 71 $ hg rm a
72 72 $ echo 'completelydifferentcontext' > b
73 73 $ hg add b
74 74 $ hg ci -m 'rm a, add b'
75 75 $ hg up -q 0
76 76 $ printf 'somecontent\nmoarcontent' > a
77 77 $ hg ci -qm 'mode a'
78 78
79 79 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
80 80 @ changeset: d526312210b9e8f795d576a77dc643796384d86e
81 81 | desc: mode a
82 82 | o changeset: 46985f76c7e5e5123433527f5c8526806145650b
83 83 |/ desc: rm a, add b
84 84 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
85 85 desc: initial
86 86
87 87 $ hg rebase -s . -d 1
88 88 rebasing 2:d526312210b9 "mode a" (tip)
89 89 other [source] changed a which local [dest] deleted
90 90 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
91 91 unresolved conflicts (see hg resolve, then hg rebase --continue)
92 92 [1]
93 93
94 94 $ cd ..
95 95 $ rm -rf repo
96 96
97 97 Test when lca didn't modified the file that was moved
98 98 -----------------------------------------------------
99 99
100 100 $ hg init repo
101 101 $ initclient repo
102 102 $ cd repo
103 103 $ echo 'somecontent' > a
104 104 $ hg add a
105 105 $ hg ci -m initial
106 106 $ echo c > c
107 107 $ hg add c
108 108 $ hg ci -m randomcommit
109 109 $ hg mv a b
110 110 $ hg ci -m 'mv a b'
111 111 $ hg up -q 1
112 112 $ echo b > a
113 113 $ hg ci -qm 'mod a'
114 114
115 115 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
116 116 @ changeset: 9d5cf99c3d9f8e8b05ba55421f7f56530cfcf3bc
117 117 | desc: mod a, phase: draft
118 118 | o changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
119 119 |/ desc: mv a b, phase: draft
120 120 o changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
121 121 | desc: randomcommit, phase: draft
122 122 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
123 123 desc: initial, phase: draft
124 124
125 125 $ hg rebase -s . -d 2
126 126 rebasing 3:9d5cf99c3d9f "mod a" (tip)
127 127 merging b and a to b
128 128 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9d5cf99c3d9f-f02358cc-rebase.hg (glob)
129 129 $ cd ..
130 130 $ rm -rf repo
131 131
132 132 Rebase "backwards"
133 133 ------------------
134 134
135 135 $ hg init repo
136 136 $ initclient repo
137 137 $ cd repo
138 138 $ echo 'somecontent' > a
139 139 $ hg add a
140 140 $ hg ci -m initial
141 141 $ echo c > c
142 142 $ hg add c
143 143 $ hg ci -m randomcommit
144 144 $ hg mv a b
145 145 $ hg ci -m 'mv a b'
146 146 $ hg up -q 2
147 147 $ echo b > b
148 148 $ hg ci -qm 'mod b'
149 149
150 150 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
151 151 @ changeset: fbe97126b3969056795c462a67d93faf13e4d298
152 152 | desc: mod b
153 153 o changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
154 154 | desc: mv a b
155 155 o changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
156 156 | desc: randomcommit
157 157 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
158 158 desc: initial
159 159
160 160 $ hg rebase -s . -d 0
161 161 rebasing 3:fbe97126b396 "mod b" (tip)
162 162 merging a and b to a
163 163 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/fbe97126b396-cf5452a1-rebase.hg (glob)
164 164 $ cd ..
165 165 $ rm -rf repo
166 166
167 167 Check a few potential move candidates
168 168 -------------------------------------
169 169
170 170 $ hg init repo
171 171 $ initclient repo
172 172 $ cd repo
173 173 $ mkdir dir
174 174 $ echo a > dir/a
175 175 $ hg add dir/a
176 176 $ hg ci -qm initial
177 177 $ hg mv dir/a dir/b
178 178 $ hg ci -qm 'mv dir/a dir/b'
179 179 $ mkdir dir2
180 180 $ echo b > dir2/a
181 181 $ hg add dir2/a
182 182 $ hg ci -qm 'create dir2/a'
183 183 $ hg up -q 0
184 184 $ echo b > dir/a
185 185 $ hg ci -qm 'mod dir/a'
186 186
187 187 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
188 188 @ changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
189 189 | desc: mod dir/a
190 190 | o changeset: 4494bf7efd2e0dfdd388e767fb913a8a3731e3fa
191 191 | | desc: create dir2/a
192 192 | o changeset: b1784dfab6ea6bfafeb11c0ac50a2981b0fe6ade
193 193 |/ desc: mv dir/a dir/b
194 194 o changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
195 195 desc: initial
196 196
197 197 $ hg rebase -s . -d 2
198 198 rebasing 3:6b2f4cece40f "mod dir/a" (tip)
199 199 merging dir/b and dir/a to dir/b
200 200 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/6b2f4cece40f-503efe60-rebase.hg (glob)
201 201 $ cd ..
202 202 $ rm -rf repo
203 203
204 Test the copytrace.movecandidateslimit with many move candidates
205 ----------------------------------------------------------------
206
207 $ hg init repo
208 $ initclient repo
209 $ cd repo
210 $ echo a > a
211 $ hg add a
212 $ hg ci -m initial
213 $ hg mv a foo
214 $ echo a > b
215 $ echo a > c
216 $ echo a > d
217 $ echo a > e
218 $ echo a > f
219 $ echo a > g
220 $ hg add b
221 $ hg add c
222 $ hg add d
223 $ hg add e
224 $ hg add f
225 $ hg add g
226 $ hg ci -m 'mv a foo, add many files'
227 $ hg up -q ".^"
228 $ echo b > a
229 $ hg ci -m 'mod a'
230 created new head
231
232 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
233 @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
234 | desc: mod a
235 | o changeset: 8329d5c6bf479ec5ca59b9864f3f45d07213f5a4
236 |/ desc: mv a foo, add many files
237 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
238 desc: initial
239
240 With small limit
241
242 $ hg rebase -s 2 -d 1 --config experimental.copytrace.movecandidateslimit=0
243 rebasing 2:ef716627c70b "mod a" (tip)
244 skipping copytracing for 'a', more candidates than the limit: 7
245 other [source] changed a which local [dest] deleted
246 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
247 unresolved conflicts (see hg resolve, then hg rebase --continue)
248 [1]
249
250 $ hg rebase --abort
251 rebase aborted
252
253 With default limit which is 100
254
255 $ hg rebase -s 2 -d 1
256 rebasing 2:ef716627c70b "mod a" (tip)
257 merging foo and a to foo
258 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob)
259
260 $ cd ..
261 $ rm -rf repo
262
204 263 Move file in one branch and delete it in another
205 264 -----------------------------------------------
206 265
207 266 $ hg init repo
208 267 $ initclient repo
209 268 $ cd repo
210 269 $ echo a > a
211 270 $ hg add a
212 271 $ hg ci -m initial
213 272 $ hg mv a b
214 273 $ hg ci -m 'mv a b'
215 274 $ hg up -q ".^"
216 275 $ hg rm a
217 276 $ hg ci -m 'del a'
218 277 created new head
219 278
220 279 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
221 280 @ changeset: 7d61ee3b1e48577891a072024968428ba465c47b
222 281 | desc: del a, phase: draft
223 282 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
224 283 |/ desc: mv a b, phase: draft
225 284 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
226 285 desc: initial, phase: draft
227 286
228 287 $ hg rebase -s 1 -d 2
229 288 rebasing 1:472e38d57782 "mv a b"
230 289 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-17d50e29-rebase.hg (glob)
231 290 $ hg up -q c492ed3c7e35dcd1dc938053b8adf56e2cfbd062
232 291 $ ls
233 292 b
234 293 $ cd ..
235 294 $ rm -rf repo
236 295
237 296 Move a directory in draft branch
238 297 --------------------------------
239 298
240 299 $ hg init repo
241 300 $ initclient repo
242 301 $ cd repo
243 302 $ mkdir dir
244 303 $ echo a > dir/a
245 304 $ hg add dir/a
246 305 $ hg ci -qm initial
247 306 $ echo b > dir/a
248 307 $ hg ci -qm 'mod dir/a'
249 308 $ hg up -q ".^"
250 309 $ hg mv -q dir/ dir2
251 310 $ hg ci -qm 'mv dir/ dir2/'
252 311
253 312 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
254 313 @ changeset: a33d80b6e352591dfd82784e1ad6cdd86b25a239
255 314 | desc: mv dir/ dir2/
256 315 | o changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
257 316 |/ desc: mod dir/a
258 317 o changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
259 318 desc: initial
260 319
261 320 $ hg rebase -s . -d 1
262 321 rebasing 2:a33d80b6e352 "mv dir/ dir2/" (tip)
263 322 merging dir/a and dir2/a to dir2/a
264 323 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/a33d80b6e352-fecb9ada-rebase.hg (glob)
265 324 $ cd ..
266 325 $ rm -rf server
267 326 $ rm -rf repo
268 327
269 328 Move file twice and rebase mod on top of moves
270 329 ----------------------------------------------
271 330
272 331 $ hg init repo
273 332 $ initclient repo
274 333 $ cd repo
275 334 $ echo a > a
276 335 $ hg add a
277 336 $ hg ci -m initial
278 337 $ hg mv a b
279 338 $ hg ci -m 'mv a b'
280 339 $ hg mv b c
281 340 $ hg ci -m 'mv b c'
282 341 $ hg up -q 0
283 342 $ echo c > a
284 343 $ hg ci -m 'mod a'
285 344 created new head
286 345
287 346 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
288 347 @ changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
289 348 | desc: mod a
290 349 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
291 350 | | desc: mv b c
292 351 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
293 352 |/ desc: mv a b
294 353 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
295 354 desc: initial
296 355 $ hg rebase -s . -d 2
297 356 rebasing 3:d41316942216 "mod a" (tip)
298 357 merging c and a to c
299 358 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d41316942216-2b5949bc-rebase.hg (glob)
300 359
301 360 $ cd ..
302 361 $ rm -rf repo
303 362
304 363 Move file twice and rebase moves on top of mods
305 364 -----------------------------------------------
306 365
307 366 $ hg init repo
308 367 $ initclient repo
309 368 $ cd repo
310 369 $ echo a > a
311 370 $ hg add a
312 371 $ hg ci -m initial
313 372 $ hg mv a b
314 373 $ hg ci -m 'mv a b'
315 374 $ hg mv b c
316 375 $ hg ci -m 'mv b c'
317 376 $ hg up -q 0
318 377 $ echo c > a
319 378 $ hg ci -m 'mod a'
320 379 created new head
321 380 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
322 381 @ changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
323 382 | desc: mod a
324 383 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
325 384 | | desc: mv b c
326 385 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
327 386 |/ desc: mv a b
328 387 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
329 388 desc: initial
330 389 $ hg rebase -s 1 -d .
331 390 rebasing 1:472e38d57782 "mv a b"
332 391 merging a and b to b
333 392 rebasing 2:d3efd280421d "mv b c"
334 393 merging b and c to c
335 394 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-ab8d3c58-rebase.hg (glob)
336 395
337 396 $ cd ..
338 397 $ rm -rf repo
339 398
340 399 Move one file and add another file in the same folder in one branch, modify file in another branch
341 400 --------------------------------------------------------------------------------------------------
342 401
343 402 $ hg init repo
344 403 $ initclient repo
345 404 $ cd repo
346 405 $ echo a > a
347 406 $ hg add a
348 407 $ hg ci -m initial
349 408 $ hg mv a b
350 409 $ hg ci -m 'mv a b'
351 410 $ echo c > c
352 411 $ hg add c
353 412 $ hg ci -m 'add c'
354 413 $ hg up -q 0
355 414 $ echo b > a
356 415 $ hg ci -m 'mod a'
357 416 created new head
358 417
359 418 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
360 419 @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
361 420 | desc: mod a
362 421 | o changeset: b1a6187e79fbce851bb584eadcb0cc4a80290fd9
363 422 | | desc: add c
364 423 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
365 424 |/ desc: mv a b
366 425 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
367 426 desc: initial
368 427
369 428 $ hg rebase -s . -d 2
370 429 rebasing 3:ef716627c70b "mod a" (tip)
371 430 merging b and a to b
372 431 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob)
373 432 $ ls
374 433 b
375 434 c
376 435 $ cat b
377 436 b
378 437 $ rm -rf repo
379 438
380 439 Merge test
381 440 ----------
382 441
383 442 $ hg init repo
384 443 $ initclient repo
385 444 $ cd repo
386 445 $ echo a > a
387 446 $ hg add a
388 447 $ hg ci -m initial
389 448 $ echo b > a
390 449 $ hg ci -m 'modify a'
391 450 $ hg up -q 0
392 451 $ hg mv a b
393 452 $ hg ci -m 'mv a b'
394 453 created new head
395 454 $ hg up -q 2
396 455
397 456 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
398 457 @ changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
399 458 | desc: mv a b
400 459 | o changeset: b0357b07f79129a3d08a68621271ca1352ae8a09
401 460 |/ desc: modify a
402 461 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
403 462 desc: initial
404 463
405 464 $ hg merge 1
406 465 merging b and a to b
407 466 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
408 467 (branch merge, don't forget to commit)
409 468 $ hg ci -m merge
410 469 $ ls
411 470 b
412 471 $ cd ..
413 472 $ rm -rf repo
414 473
415 474 Copy and move file
416 475 ------------------
417 476
418 477 $ hg init repo
419 478 $ initclient repo
420 479 $ cd repo
421 480 $ echo a > a
422 481 $ hg add a
423 482 $ hg ci -m initial
424 483 $ hg cp a c
425 484 $ hg mv a b
426 485 $ hg ci -m 'cp a c, mv a b'
427 486 $ hg up -q 0
428 487 $ echo b > a
429 488 $ hg ci -m 'mod a'
430 489 created new head
431 490
432 491 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
433 492 @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
434 493 | desc: mod a
435 494 | o changeset: 4fc3fd13fbdb89ada6b75bfcef3911a689a0dde8
436 495 |/ desc: cp a c, mv a b
437 496 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
438 497 desc: initial
439 498
440 499 $ hg rebase -s . -d 1
441 500 rebasing 2:ef716627c70b "mod a" (tip)
442 501 merging b and a to b
443 502 merging c and a to c
444 503 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob)
445 504 $ ls
446 505 b
447 506 c
448 507 $ cat b
449 508 b
450 509 $ cat c
451 510 b
452 511 $ cd ..
453 512 $ rm -rf repo
454 513
455 514 Do a merge commit with many consequent moves in one branch
456 515 ----------------------------------------------------------
457 516
458 517 $ hg init repo
459 518 $ initclient repo
460 519 $ cd repo
461 520 $ echo a > a
462 521 $ hg add a
463 522 $ hg ci -m initial
464 523 $ echo b > a
465 524 $ hg ci -qm 'mod a'
466 525 $ hg up -q ".^"
467 526 $ hg mv a b
468 527 $ hg ci -qm 'mv a b'
469 528 $ hg mv b c
470 529 $ hg ci -qm 'mv b c'
471 530 $ hg up -q 1
472 531 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
473 532 o changeset: d3efd280421d24f9f229997c19e654761c942a71
474 533 | desc: mv b c
475 534 o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
476 535 | desc: mv a b
477 536 | @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
478 537 |/ desc: mod a
479 538 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
480 539 desc: initial
481 540
482 541 $ hg merge 3
483 542 merging a and c to c
484 543 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
485 544 (branch merge, don't forget to commit)
486 545 $ hg ci -qm 'merge'
487 546 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
488 547 @ changeset: cd29b0d08c0f39bfed4cde1b40e30f419db0c825
489 548 |\ desc: merge, phase: draft
490 549 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
491 550 | | desc: mv b c, phase: draft
492 551 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
493 552 | | desc: mv a b, phase: draft
494 553 o | changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
495 554 |/ desc: mod a, phase: draft
496 555 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
497 556 desc: initial, phase: draft
498 557 $ ls
499 558 c
500 559 $ cd ..
501 560 $ rm -rf repo
502 561
503 562 Test shelve/unshelve
504 563 -------------------
505 564
506 565 $ hg init repo
507 566 $ initclient repo
508 567 $ cd repo
509 568 $ echo a > a
510 569 $ hg add a
511 570 $ hg ci -m initial
512 571 $ echo b > a
513 572 $ hg shelve
514 573 shelved as default
515 574 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
516 575 $ hg mv a b
517 576 $ hg ci -m 'mv a b'
518 577
519 578 $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
520 579 @ changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
521 580 | desc: mv a b
522 581 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
523 582 desc: initial
524 583 $ hg unshelve
525 584 unshelving change 'default'
526 585 rebasing shelved changes
527 586 rebasing 2:45f63161acea "changes to: initial" (tip)
528 587 merging b and a to b
529 588 $ ls
530 589 b
531 590 $ cat b
532 591 b
533 592 $ cd ..
534 593 $ rm -rf repo
535 594
536 595 Test full copytrace ability on draft branch
537 596 -------------------------------------------
538 597
539 598 File directory and base name changed in same move
540 599 $ hg init repo
541 600 $ initclient repo
542 601 $ mkdir repo/dir1
543 602 $ cd repo/dir1
544 603 $ echo a > a
545 604 $ hg add a
546 605 $ hg ci -qm initial
547 606 $ cd ..
548 607 $ hg mv -q dir1 dir2
549 608 $ hg mv dir2/a dir2/b
550 609 $ hg ci -qm 'mv a b; mv dir1 dir2'
551 610 $ hg up -q '.^'
552 611 $ cd dir1
553 612 $ echo b >> a
554 613 $ cd ..
555 614 $ hg ci -qm 'mod a'
556 615
557 616 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
558 617 @ changeset 6207d2d318e710b882e3d5ada2a89770efc42c96
559 618 | desc mod a, phase: draft
560 619 | o changeset abffdd4e3dfc04bc375034b970299b2a309a1cce
561 620 |/ desc mv a b; mv dir1 dir2, phase: draft
562 621 o changeset 81973cd24b58db2fdf18ce3d64fb2cc3284e9ab3
563 622 desc initial, phase: draft
564 623
565 624 $ hg rebase -s . -d 1 --config experimental.copytrace.sourcecommitlimit=100
566 625 rebasing 2:6207d2d318e7 "mod a" (tip)
567 626 merging dir2/b and dir1/a to dir2/b
568 627 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/6207d2d318e7-1c9779ad-rebase.hg (glob)
569 628 $ cat dir2/b
570 629 a
571 630 b
572 631 $ cd ..
573 632 $ rm -rf repo
574 633
575 634 Move directory in one merge parent, while adding file to original directory
576 635 in other merge parent. File moved on rebase.
577 636
578 637 $ hg init repo
579 638 $ initclient repo
580 639 $ mkdir repo/dir1
581 640 $ cd repo/dir1
582 641 $ echo dummy > dummy
583 642 $ hg add dummy
584 643 $ cd ..
585 644 $ hg ci -qm initial
586 645 $ cd dir1
587 646 $ echo a > a
588 647 $ hg add a
589 648 $ cd ..
590 649 $ hg ci -qm 'hg add dir1/a'
591 650 $ hg up -q '.^'
592 651 $ hg mv -q dir1 dir2
593 652 $ hg ci -qm 'mv dir1 dir2'
594 653
595 654 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
596 655 @ changeset e8919e7df8d036e07b906045eddcd4a42ff1915f
597 656 | desc mv dir1 dir2, phase: draft
598 657 | o changeset 7c7c6f339be00f849c3cb2df738ca91db78b32c8
599 658 |/ desc hg add dir1/a, phase: draft
600 659 o changeset a235dcce55dcf42034c4e374cb200662d0bb4a13
601 660 desc initial, phase: draft
602 661
603 662 $ hg rebase -s . -d 1 --config experimental.copytrace.sourcecommitlimit=100
604 663 rebasing 2:e8919e7df8d0 "mv dir1 dir2" (tip)
605 664 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/e8919e7df8d0-f62fab62-rebase.hg (glob)
606 665 $ ls dir2
607 666 a
608 667 dummy
609 668 $ rm -rf repo
610 669
611 670 Testing the sourcecommitlimit config
612 671 -----------------------------------
613 672
614 673 $ hg init repo
615 674 $ initclient repo
616 675 $ cd repo
617 676 $ echo a > a
618 677 $ hg ci -Aqm "added a"
619 678 $ echo "more things" >> a
620 679 $ hg ci -qm "added more things to a"
621 680 $ hg up 0
622 681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
623 682 $ echo b > b
624 683 $ hg ci -Aqm "added b"
625 684 $ mkdir foo
626 685 $ hg mv a foo/bar
627 686 $ hg ci -m "Moved a to foo/bar"
628 687 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
629 688 @ changeset b4b0f7880e500b5c364a5f07b4a2b167de7a6fb0
630 689 | desc Moved a to foo/bar, phase: draft
631 690 o changeset 5f6d8a4bf34ab274ccc9f631c2536964b8a3666d
632 691 | desc added b, phase: draft
633 692 | o changeset 8b6e13696c38e8445a759516474640c2f8dddef6
634 693 |/ desc added more things to a, phase: draft
635 694 o changeset 9092f1db7931481f93b37d5c9fbcfc341bcd7318
636 695 desc added a, phase: draft
637 696
638 697 When the sourcecommitlimit is small and we have more drafts, we use heuristics only
639 698
640 699 $ hg rebase -s 8b6e13696 -d .
641 700 rebasing 1:8b6e13696c38 "added more things to a"
642 701 other [source] changed a which local [dest] deleted
643 702 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
644 703 unresolved conflicts (see hg resolve, then hg rebase --continue)
645 704 [1]
646 705
647 706 But when we have "sourcecommitlimit > (no. of drafts from base to c1)", we do
648 707 fullcopytracing
649 708
650 709 $ hg rebase --abort
651 710 rebase aborted
652 711 $ hg rebase -s 8b6e13696 -d . --config experimental.copytrace.sourcecommitlimit=100
653 712 rebasing 1:8b6e13696c38 "added more things to a"
654 713 merging foo/bar and a to foo/bar
655 714 saved backup bundle to $TESTTMP/repo/repo/repo/.hg/strip-backup/8b6e13696c38-fc14ac83-rebase.hg (glob)
656 715 $ cd ..
657 716 $ rm -rf repo
General Comments 0
You need to be logged in to leave comments. Login now