##// END OF EJS Templates
copytrace: replace experimental.disablecopytrace config with copytrace (BC)...
Pulkit Goyal -
r34079:26531db4 default
parent child Browse files
Show More
@@ -1,601 +1,601 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
12 12 from . import (
13 13 error,
14 14 )
15 15
16 16 def loadconfigtable(ui, extname, configtable):
17 17 """update config item known to the ui with the extension ones"""
18 18 for section, items in configtable.items():
19 19 knownitems = ui._knownconfig.setdefault(section, {})
20 20 knownkeys = set(knownitems)
21 21 newkeys = set(items)
22 22 for key in sorted(knownkeys & newkeys):
23 23 msg = "extension '%s' overwrite config item '%s.%s'"
24 24 msg %= (extname, section, key)
25 25 ui.develwarn(msg, config='warn-config')
26 26
27 27 knownitems.update(items)
28 28
29 29 class configitem(object):
30 30 """represent a known config item
31 31
32 32 :section: the official config section where to find this item,
33 33 :name: the official name within the section,
34 34 :default: default value for this item,
35 35 :alias: optional list of tuples as alternatives.
36 36 """
37 37
38 38 def __init__(self, section, name, default=None, alias=()):
39 39 self.section = section
40 40 self.name = name
41 41 self.default = default
42 42 self.alias = list(alias)
43 43
44 44 coreitems = {}
45 45
46 46 def _register(configtable, *args, **kwargs):
47 47 item = configitem(*args, **kwargs)
48 48 section = configtable.setdefault(item.section, {})
49 49 if item.name in section:
50 50 msg = "duplicated config item registration for '%s.%s'"
51 51 raise error.ProgrammingError(msg % (item.section, item.name))
52 52 section[item.name] = item
53 53
54 54 # special value for case where the default is derived from other values
55 55 dynamicdefault = object()
56 56
57 57 # Registering actual config items
58 58
59 59 def getitemregister(configtable):
60 60 return functools.partial(_register, configtable)
61 61
62 62 coreconfigitem = getitemregister(coreitems)
63 63
64 64 coreconfigitem('auth', 'cookiefile',
65 65 default=None,
66 66 )
67 67 # bookmarks.pushing: internal hack for discovery
68 68 coreconfigitem('bookmarks', 'pushing',
69 69 default=list,
70 70 )
71 71 # bundle.mainreporoot: internal hack for bundlerepo
72 72 coreconfigitem('bundle', 'mainreporoot',
73 73 default='',
74 74 )
75 75 # bundle.reorder: experimental config
76 76 coreconfigitem('bundle', 'reorder',
77 77 default='auto',
78 78 )
79 79 coreconfigitem('censor', 'policy',
80 80 default='abort',
81 81 )
82 82 coreconfigitem('chgserver', 'idletimeout',
83 83 default=3600,
84 84 )
85 85 coreconfigitem('chgserver', 'skiphash',
86 86 default=False,
87 87 )
88 88 coreconfigitem('cmdserver', 'log',
89 89 default=None,
90 90 )
91 91 coreconfigitem('color', 'mode',
92 92 default='auto',
93 93 )
94 94 coreconfigitem('color', 'pagermode',
95 95 default=dynamicdefault,
96 96 )
97 97 coreconfigitem('commands', 'status.relative',
98 98 default=False,
99 99 )
100 100 coreconfigitem('commands', 'status.skipstates',
101 101 default=[],
102 102 )
103 103 coreconfigitem('commands', 'status.verbose',
104 104 default=False,
105 105 )
106 106 coreconfigitem('commands', 'update.requiredest',
107 107 default=False,
108 108 )
109 109 coreconfigitem('devel', 'all-warnings',
110 110 default=False,
111 111 )
112 112 coreconfigitem('devel', 'bundle2.debug',
113 113 default=False,
114 114 )
115 115 coreconfigitem('devel', 'check-locks',
116 116 default=False,
117 117 )
118 118 coreconfigitem('devel', 'check-relroot',
119 119 default=False,
120 120 )
121 121 coreconfigitem('devel', 'default-date',
122 122 default=None,
123 123 )
124 124 coreconfigitem('devel', 'deprec-warn',
125 125 default=False,
126 126 )
127 127 coreconfigitem('devel', 'disableloaddefaultcerts',
128 128 default=False,
129 129 )
130 130 coreconfigitem('devel', 'legacy.exchange',
131 131 default=list,
132 132 )
133 133 coreconfigitem('devel', 'servercafile',
134 134 default='',
135 135 )
136 136 coreconfigitem('devel', 'serverexactprotocol',
137 137 default='',
138 138 )
139 139 coreconfigitem('devel', 'serverrequirecert',
140 140 default=False,
141 141 )
142 142 coreconfigitem('devel', 'strip-obsmarkers',
143 143 default=True,
144 144 )
145 145 coreconfigitem('email', 'charsets',
146 146 default=list,
147 147 )
148 148 coreconfigitem('email', 'method',
149 149 default='smtp',
150 150 )
151 151 coreconfigitem('experimental', 'bundle-phases',
152 152 default=False,
153 153 )
154 154 coreconfigitem('experimental', 'bundle2-advertise',
155 155 default=True,
156 156 )
157 157 coreconfigitem('experimental', 'bundle2-output-capture',
158 158 default=False,
159 159 )
160 160 coreconfigitem('experimental', 'bundle2.pushback',
161 161 default=False,
162 162 )
163 163 coreconfigitem('experimental', 'bundle2lazylocking',
164 164 default=False,
165 165 )
166 166 coreconfigitem('experimental', 'bundlecomplevel',
167 167 default=None,
168 168 )
169 169 coreconfigitem('experimental', 'changegroup3',
170 170 default=False,
171 171 )
172 172 coreconfigitem('experimental', 'clientcompressionengines',
173 173 default=list,
174 174 )
175 coreconfigitem('experimental', 'copytrace',
176 default='on',
177 )
175 178 coreconfigitem('experimental', 'crecordtest',
176 179 default=None,
177 180 )
178 coreconfigitem('experimental', 'disablecopytrace',
179 default=False,
180 )
181 181 coreconfigitem('experimental', 'editortmpinhg',
182 182 default=False,
183 183 )
184 184 coreconfigitem('experimental', 'stabilization',
185 185 default=list,
186 186 alias=[('experimental', 'evolution')],
187 187 )
188 188 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
189 189 default=False,
190 190 alias=[('experimental', 'evolution.bundle-obsmarker')],
191 191 )
192 192 coreconfigitem('experimental', 'stabilization.track-operation',
193 193 default=False,
194 194 alias=[('experimental', 'evolution.track-operation')]
195 195 )
196 196 coreconfigitem('experimental', 'exportableenviron',
197 197 default=list,
198 198 )
199 199 coreconfigitem('experimental', 'extendedheader.index',
200 200 default=None,
201 201 )
202 202 coreconfigitem('experimental', 'extendedheader.similarity',
203 203 default=False,
204 204 )
205 205 coreconfigitem('experimental', 'format.compression',
206 206 default='zlib',
207 207 )
208 208 coreconfigitem('experimental', 'graphshorten',
209 209 default=False,
210 210 )
211 211 coreconfigitem('experimental', 'hook-track-tags',
212 212 default=False,
213 213 )
214 214 coreconfigitem('experimental', 'httppostargs',
215 215 default=False,
216 216 )
217 217 coreconfigitem('experimental', 'manifestv2',
218 218 default=False,
219 219 )
220 220 coreconfigitem('experimental', 'mergedriver',
221 221 default=None,
222 222 )
223 223 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
224 224 default=False,
225 225 )
226 226 coreconfigitem('experimental', 'rebase.multidest',
227 227 default=False,
228 228 )
229 229 coreconfigitem('experimental', 'revertalternateinteractivemode',
230 230 default=True,
231 231 )
232 232 coreconfigitem('experimental', 'revlogv2',
233 233 default=None,
234 234 )
235 235 coreconfigitem('experimental', 'spacemovesdown',
236 236 default=False,
237 237 )
238 238 coreconfigitem('experimental', 'treemanifest',
239 239 default=False,
240 240 )
241 241 coreconfigitem('experimental', 'updatecheck',
242 242 default=None,
243 243 )
244 244 coreconfigitem('format', 'aggressivemergedeltas',
245 245 default=False,
246 246 )
247 247 coreconfigitem('format', 'chunkcachesize',
248 248 default=None,
249 249 )
250 250 coreconfigitem('format', 'dotencode',
251 251 default=True,
252 252 )
253 253 coreconfigitem('format', 'generaldelta',
254 254 default=False,
255 255 )
256 256 coreconfigitem('format', 'manifestcachesize',
257 257 default=None,
258 258 )
259 259 coreconfigitem('format', 'maxchainlen',
260 260 default=None,
261 261 )
262 262 coreconfigitem('format', 'obsstore-version',
263 263 default=None,
264 264 )
265 265 coreconfigitem('format', 'usefncache',
266 266 default=True,
267 267 )
268 268 coreconfigitem('format', 'usegeneraldelta',
269 269 default=True,
270 270 )
271 271 coreconfigitem('format', 'usestore',
272 272 default=True,
273 273 )
274 274 coreconfigitem('hostsecurity', 'ciphers',
275 275 default=None,
276 276 )
277 277 coreconfigitem('hostsecurity', 'disabletls10warning',
278 278 default=False,
279 279 )
280 280 coreconfigitem('http_proxy', 'always',
281 281 default=False,
282 282 )
283 283 coreconfigitem('http_proxy', 'host',
284 284 default=None,
285 285 )
286 286 coreconfigitem('http_proxy', 'no',
287 287 default=list,
288 288 )
289 289 coreconfigitem('http_proxy', 'passwd',
290 290 default=None,
291 291 )
292 292 coreconfigitem('http_proxy', 'user',
293 293 default=None,
294 294 )
295 295 coreconfigitem('merge', 'followcopies',
296 296 default=True,
297 297 )
298 298 coreconfigitem('pager', 'ignore',
299 299 default=list,
300 300 )
301 301 coreconfigitem('patch', 'eol',
302 302 default='strict',
303 303 )
304 304 coreconfigitem('patch', 'fuzz',
305 305 default=2,
306 306 )
307 307 coreconfigitem('paths', 'default',
308 308 default=None,
309 309 )
310 310 coreconfigitem('paths', 'default-push',
311 311 default=None,
312 312 )
313 313 coreconfigitem('phases', 'checksubrepos',
314 314 default='follow',
315 315 )
316 316 coreconfigitem('phases', 'publish',
317 317 default=True,
318 318 )
319 319 coreconfigitem('profiling', 'enabled',
320 320 default=False,
321 321 )
322 322 coreconfigitem('profiling', 'format',
323 323 default='text',
324 324 )
325 325 coreconfigitem('profiling', 'freq',
326 326 default=1000,
327 327 )
328 328 coreconfigitem('profiling', 'limit',
329 329 default=30,
330 330 )
331 331 coreconfigitem('profiling', 'nested',
332 332 default=0,
333 333 )
334 334 coreconfigitem('profiling', 'sort',
335 335 default='inlinetime',
336 336 )
337 337 coreconfigitem('profiling', 'statformat',
338 338 default='hotpath',
339 339 )
340 340 coreconfigitem('progress', 'assume-tty',
341 341 default=False,
342 342 )
343 343 coreconfigitem('progress', 'changedelay',
344 344 default=1,
345 345 )
346 346 coreconfigitem('progress', 'clear-complete',
347 347 default=True,
348 348 )
349 349 coreconfigitem('progress', 'debug',
350 350 default=False,
351 351 )
352 352 coreconfigitem('progress', 'delay',
353 353 default=3,
354 354 )
355 355 coreconfigitem('progress', 'disable',
356 356 default=False,
357 357 )
358 358 coreconfigitem('progress', 'estimate',
359 359 default=2,
360 360 )
361 361 coreconfigitem('progress', 'refresh',
362 362 default=0.1,
363 363 )
364 364 coreconfigitem('progress', 'width',
365 365 default=dynamicdefault,
366 366 )
367 367 coreconfigitem('push', 'pushvars.server',
368 368 default=False,
369 369 )
370 370 coreconfigitem('server', 'bundle1',
371 371 default=True,
372 372 )
373 373 coreconfigitem('server', 'bundle1gd',
374 374 default=None,
375 375 )
376 376 coreconfigitem('server', 'compressionengines',
377 377 default=list,
378 378 )
379 379 coreconfigitem('server', 'concurrent-push-mode',
380 380 default='strict',
381 381 )
382 382 coreconfigitem('server', 'disablefullbundle',
383 383 default=False,
384 384 )
385 385 coreconfigitem('server', 'maxhttpheaderlen',
386 386 default=1024,
387 387 )
388 388 coreconfigitem('server', 'preferuncompressed',
389 389 default=False,
390 390 )
391 391 coreconfigitem('server', 'uncompressed',
392 392 default=True,
393 393 )
394 394 coreconfigitem('server', 'uncompressedallowsecret',
395 395 default=False,
396 396 )
397 397 coreconfigitem('server', 'validate',
398 398 default=False,
399 399 )
400 400 coreconfigitem('server', 'zliblevel',
401 401 default=-1,
402 402 )
403 403 coreconfigitem('smtp', 'host',
404 404 default=None,
405 405 )
406 406 coreconfigitem('smtp', 'local_hostname',
407 407 default=None,
408 408 )
409 409 coreconfigitem('smtp', 'password',
410 410 default=None,
411 411 )
412 412 coreconfigitem('smtp', 'tls',
413 413 default='none',
414 414 )
415 415 coreconfigitem('smtp', 'username',
416 416 default=None,
417 417 )
418 418 coreconfigitem('sparse', 'missingwarning',
419 419 default=True,
420 420 )
421 421 coreconfigitem('trusted', 'groups',
422 422 default=list,
423 423 )
424 424 coreconfigitem('trusted', 'users',
425 425 default=list,
426 426 )
427 427 coreconfigitem('ui', '_usedassubrepo',
428 428 default=False,
429 429 )
430 430 coreconfigitem('ui', 'allowemptycommit',
431 431 default=False,
432 432 )
433 433 coreconfigitem('ui', 'archivemeta',
434 434 default=True,
435 435 )
436 436 coreconfigitem('ui', 'askusername',
437 437 default=False,
438 438 )
439 439 coreconfigitem('ui', 'clonebundlefallback',
440 440 default=False,
441 441 )
442 442 coreconfigitem('ui', 'clonebundleprefers',
443 443 default=list,
444 444 )
445 445 coreconfigitem('ui', 'clonebundles',
446 446 default=True,
447 447 )
448 448 coreconfigitem('ui', 'color',
449 449 default='auto',
450 450 )
451 451 coreconfigitem('ui', 'commitsubrepos',
452 452 default=False,
453 453 )
454 454 coreconfigitem('ui', 'debug',
455 455 default=False,
456 456 )
457 457 coreconfigitem('ui', 'debugger',
458 458 default=None,
459 459 )
460 460 coreconfigitem('ui', 'fallbackencoding',
461 461 default=None,
462 462 )
463 463 coreconfigitem('ui', 'forcecwd',
464 464 default=None,
465 465 )
466 466 coreconfigitem('ui', 'forcemerge',
467 467 default=None,
468 468 )
469 469 coreconfigitem('ui', 'formatdebug',
470 470 default=False,
471 471 )
472 472 coreconfigitem('ui', 'formatjson',
473 473 default=False,
474 474 )
475 475 coreconfigitem('ui', 'formatted',
476 476 default=None,
477 477 )
478 478 coreconfigitem('ui', 'graphnodetemplate',
479 479 default=None,
480 480 )
481 481 coreconfigitem('ui', 'http2debuglevel',
482 482 default=None,
483 483 )
484 484 coreconfigitem('ui', 'interactive',
485 485 default=None,
486 486 )
487 487 coreconfigitem('ui', 'interface',
488 488 default=None,
489 489 )
490 490 coreconfigitem('ui', 'logblockedtimes',
491 491 default=False,
492 492 )
493 493 coreconfigitem('ui', 'logtemplate',
494 494 default=None,
495 495 )
496 496 coreconfigitem('ui', 'merge',
497 497 default=None,
498 498 )
499 499 coreconfigitem('ui', 'mergemarkers',
500 500 default='basic',
501 501 )
502 502 coreconfigitem('ui', 'mergemarkertemplate',
503 503 default=('{node|short} '
504 504 '{ifeq(tags, "tip", "", '
505 505 'ifeq(tags, "", "", "{tags} "))}'
506 506 '{if(bookmarks, "{bookmarks} ")}'
507 507 '{ifeq(branch, "default", "", "{branch} ")}'
508 508 '- {author|user}: {desc|firstline}')
509 509 )
510 510 coreconfigitem('ui', 'nontty',
511 511 default=False,
512 512 )
513 513 coreconfigitem('ui', 'origbackuppath',
514 514 default=None,
515 515 )
516 516 coreconfigitem('ui', 'paginate',
517 517 default=True,
518 518 )
519 519 coreconfigitem('ui', 'patch',
520 520 default=None,
521 521 )
522 522 coreconfigitem('ui', 'portablefilenames',
523 523 default='warn',
524 524 )
525 525 coreconfigitem('ui', 'promptecho',
526 526 default=False,
527 527 )
528 528 coreconfigitem('ui', 'quiet',
529 529 default=False,
530 530 )
531 531 coreconfigitem('ui', 'quietbookmarkmove',
532 532 default=False,
533 533 )
534 534 coreconfigitem('ui', 'remotecmd',
535 535 default='hg',
536 536 )
537 537 coreconfigitem('ui', 'report_untrusted',
538 538 default=True,
539 539 )
540 540 coreconfigitem('ui', 'rollback',
541 541 default=True,
542 542 )
543 543 coreconfigitem('ui', 'slash',
544 544 default=False,
545 545 )
546 546 coreconfigitem('ui', 'ssh',
547 547 default='ssh',
548 548 )
549 549 coreconfigitem('ui', 'statuscopies',
550 550 default=False,
551 551 )
552 552 coreconfigitem('ui', 'strict',
553 553 default=False,
554 554 )
555 555 coreconfigitem('ui', 'style',
556 556 default='',
557 557 )
558 558 coreconfigitem('ui', 'supportcontact',
559 559 default=None,
560 560 )
561 561 coreconfigitem('ui', 'textwidth',
562 562 default=78,
563 563 )
564 564 coreconfigitem('ui', 'timeout',
565 565 default='600',
566 566 )
567 567 coreconfigitem('ui', 'traceback',
568 568 default=False,
569 569 )
570 570 coreconfigitem('ui', 'tweakdefaults',
571 571 default=False,
572 572 )
573 573 coreconfigitem('ui', 'usehttp2',
574 574 default=False,
575 575 )
576 576 coreconfigitem('ui', 'username',
577 577 alias=[('ui', 'user')]
578 578 )
579 579 coreconfigitem('ui', 'verbose',
580 580 default=False,
581 581 )
582 582 coreconfigitem('verify', 'skipflags',
583 583 default=None,
584 584 )
585 585 coreconfigitem('worker', 'backgroundclose',
586 586 default=dynamicdefault,
587 587 )
588 588 # Windows defaults to a limit of 512 open files. A buffer of 128
589 589 # should give us enough headway.
590 590 coreconfigitem('worker', 'backgroundclosemaxqueue',
591 591 default=384,
592 592 )
593 593 coreconfigitem('worker', 'backgroundcloseminfilecount',
594 594 default=2048,
595 595 )
596 596 coreconfigitem('worker', 'backgroundclosethreadcount',
597 597 default=4,
598 598 )
599 599 coreconfigitem('worker', 'numcpus',
600 600 default=None,
601 601 )
@@ -1,743 +1,743 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 heapq
11 11
12 12 from . import (
13 13 match as matchmod,
14 14 node,
15 15 pathutil,
16 16 scmutil,
17 17 util,
18 18 )
19 19
20 20 def _findlimit(repo, a, b):
21 21 """
22 22 Find the last revision that needs to be checked to ensure that a full
23 23 transitive closure for file copies can be properly calculated.
24 24 Generally, this means finding the earliest revision number that's an
25 25 ancestor of a or b but not both, except when a or b is a direct descendent
26 26 of the other, in which case we can return the minimum revnum of a and b.
27 27 None if no such revision exists.
28 28 """
29 29
30 30 # basic idea:
31 31 # - mark a and b with different sides
32 32 # - if a parent's children are all on the same side, the parent is
33 33 # on that side, otherwise it is on no side
34 34 # - walk the graph in topological order with the help of a heap;
35 35 # - add unseen parents to side map
36 36 # - clear side of any parent that has children on different sides
37 37 # - track number of interesting revs that might still be on a side
38 38 # - track the lowest interesting rev seen
39 39 # - quit when interesting revs is zero
40 40
41 41 cl = repo.changelog
42 42 working = len(cl) # pseudo rev for the working directory
43 43 if a is None:
44 44 a = working
45 45 if b is None:
46 46 b = working
47 47
48 48 side = {a: -1, b: 1}
49 49 visit = [-a, -b]
50 50 heapq.heapify(visit)
51 51 interesting = len(visit)
52 52 hascommonancestor = False
53 53 limit = working
54 54
55 55 while interesting:
56 56 r = -heapq.heappop(visit)
57 57 if r == working:
58 58 parents = [cl.rev(p) for p in repo.dirstate.parents()]
59 59 else:
60 60 parents = cl.parentrevs(r)
61 61 for p in parents:
62 62 if p < 0:
63 63 continue
64 64 if p not in side:
65 65 # first time we see p; add it to visit
66 66 side[p] = side[r]
67 67 if side[p]:
68 68 interesting += 1
69 69 heapq.heappush(visit, -p)
70 70 elif side[p] and side[p] != side[r]:
71 71 # p was interesting but now we know better
72 72 side[p] = 0
73 73 interesting -= 1
74 74 hascommonancestor = True
75 75 if side[r]:
76 76 limit = r # lowest rev visited
77 77 interesting -= 1
78 78
79 79 if not hascommonancestor:
80 80 return None
81 81
82 82 # Consider the following flow (see test-commit-amend.t under issue4405):
83 83 # 1/ File 'a0' committed
84 84 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
85 85 # 3/ Move back to first commit
86 86 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
87 87 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
88 88 #
89 89 # During the amend in step five, we will be in this state:
90 90 #
91 91 # @ 3 temporary amend commit for a1-amend
92 92 # |
93 93 # o 2 a1-amend
94 94 # |
95 95 # | o 1 a1
96 96 # |/
97 97 # o 0 a0
98 98 #
99 99 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
100 100 # yet the filelog has the copy information in rev 1 and we will not look
101 101 # back far enough unless we also look at the a and b as candidates.
102 102 # This only occurs when a is a descendent of b or visa-versa.
103 103 return min(limit, a, b)
104 104
105 105 def _chain(src, dst, a, b):
106 106 '''chain two sets of copies a->b'''
107 107 t = a.copy()
108 108 for k, v in b.iteritems():
109 109 if v in t:
110 110 # found a chain
111 111 if t[v] != k:
112 112 # file wasn't renamed back to itself
113 113 t[k] = t[v]
114 114 if v not in dst:
115 115 # chain was a rename, not a copy
116 116 del t[v]
117 117 if v in src:
118 118 # file is a copy of an existing file
119 119 t[k] = v
120 120
121 121 # remove criss-crossed copies
122 122 for k, v in t.items():
123 123 if k in src and v in dst:
124 124 del t[k]
125 125
126 126 return t
127 127
128 128 def _tracefile(fctx, am, limit=-1):
129 129 '''return file context that is the ancestor of fctx present in ancestor
130 130 manifest am, stopping after the first ancestor lower than limit'''
131 131
132 132 for f in fctx.ancestors():
133 133 if am.get(f.path(), None) == f.filenode():
134 134 return f
135 135 if limit >= 0 and f.linkrev() < limit and f.rev() < limit:
136 136 return None
137 137
138 138 def _dirstatecopies(d):
139 139 ds = d._repo.dirstate
140 140 c = ds.copies().copy()
141 141 for k in c.keys():
142 142 if ds[k] not in 'anm':
143 143 del c[k]
144 144 return c
145 145
146 146 def _computeforwardmissing(a, b, match=None):
147 147 """Computes which files are in b but not a.
148 148 This is its own function so extensions can easily wrap this call to see what
149 149 files _forwardcopies is about to process.
150 150 """
151 151 ma = a.manifest()
152 152 mb = b.manifest()
153 153 return mb.filesnotin(ma, match=match)
154 154
155 155 def _forwardcopies(a, b, match=None):
156 156 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
157 157
158 158 # check for working copy
159 159 w = None
160 160 if b.rev() is None:
161 161 w = b
162 162 b = w.p1()
163 163 if a == b:
164 164 # short-circuit to avoid issues with merge states
165 165 return _dirstatecopies(w)
166 166
167 167 # files might have to be traced back to the fctx parent of the last
168 168 # one-side-only changeset, but not further back than that
169 169 limit = _findlimit(a._repo, a.rev(), b.rev())
170 170 if limit is None:
171 171 limit = -1
172 172 am = a.manifest()
173 173
174 174 # find where new files came from
175 175 # we currently don't try to find where old files went, too expensive
176 176 # this means we can miss a case like 'hg rm b; hg cp a b'
177 177 cm = {}
178 178
179 179 # Computing the forward missing is quite expensive on large manifests, since
180 180 # it compares the entire manifests. We can optimize it in the common use
181 181 # case of computing what copies are in a commit versus its parent (like
182 182 # during a rebase or histedit). Note, we exclude merge commits from this
183 183 # optimization, since the ctx.files() for a merge commit is not correct for
184 184 # this comparison.
185 185 forwardmissingmatch = match
186 186 if b.p1() == a and b.p2().node() == node.nullid:
187 187 filesmatcher = scmutil.matchfiles(a._repo, b.files())
188 188 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
189 189 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
190 190
191 191 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
192 192 for f in missing:
193 193 fctx = b[f]
194 194 fctx._ancestrycontext = ancestrycontext
195 195 ofctx = _tracefile(fctx, am, limit)
196 196 if ofctx:
197 197 cm[f] = ofctx.path()
198 198
199 199 # combine copies from dirstate if necessary
200 200 if w is not None:
201 201 cm = _chain(a, w, cm, _dirstatecopies(w))
202 202
203 203 return cm
204 204
205 205 def _backwardrenames(a, b):
206 if a._repo.ui.configbool('experimental', 'disablecopytrace'):
206 if a._repo.ui.config('experimental', 'copytrace') == 'off':
207 207 return {}
208 208
209 209 # Even though we're not taking copies into account, 1:n rename situations
210 210 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
211 211 # arbitrarily pick one of the renames.
212 212 f = _forwardcopies(b, a)
213 213 r = {}
214 214 for k, v in sorted(f.iteritems()):
215 215 # remove copies
216 216 if v in a:
217 217 continue
218 218 r[v] = k
219 219 return r
220 220
221 221 def pathcopies(x, y, match=None):
222 222 '''find {dst@y: src@x} copy mapping for directed compare'''
223 223 if x == y or not x or not y:
224 224 return {}
225 225 a = y.ancestor(x)
226 226 if a == x:
227 227 return _forwardcopies(x, y, match=match)
228 228 if a == y:
229 229 return _backwardrenames(x, y)
230 230 return _chain(x, y, _backwardrenames(x, a),
231 231 _forwardcopies(a, y, match=match))
232 232
233 233 def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
234 234 """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
235 235 and c2. This is its own function so extensions can easily wrap this call
236 236 to see what files mergecopies is about to process.
237 237
238 238 Even though c1 and c2 are not used in this function, they are useful in
239 239 other extensions for being able to read the file nodes of the changed files.
240 240
241 241 "baselabel" can be passed to help distinguish the multiple computations
242 242 done in the graft case.
243 243 """
244 244 u1 = sorted(addedinm1 - addedinm2)
245 245 u2 = sorted(addedinm2 - addedinm1)
246 246
247 247 header = " unmatched files in %s"
248 248 if baselabel:
249 249 header += ' (from %s)' % baselabel
250 250 if u1:
251 251 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1)))
252 252 if u2:
253 253 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2)))
254 254 return u1, u2
255 255
256 256 def _makegetfctx(ctx):
257 257 """return a 'getfctx' function suitable for _checkcopies usage
258 258
259 259 We have to re-setup the function building 'filectx' for each
260 260 '_checkcopies' to ensure the linkrev adjustment is properly setup for
261 261 each. Linkrev adjustment is important to avoid bug in rename
262 262 detection. Moreover, having a proper '_ancestrycontext' setup ensures
263 263 the performance impact of this adjustment is kept limited. Without it,
264 264 each file could do a full dag traversal making the time complexity of
265 265 the operation explode (see issue4537).
266 266
267 267 This function exists here mostly to limit the impact on stable. Feel
268 268 free to refactor on default.
269 269 """
270 270 rev = ctx.rev()
271 271 repo = ctx._repo
272 272 ac = getattr(ctx, '_ancestrycontext', None)
273 273 if ac is None:
274 274 revs = [rev]
275 275 if rev is None:
276 276 revs = [p.rev() for p in ctx.parents()]
277 277 ac = repo.changelog.ancestors(revs, inclusive=True)
278 278 ctx._ancestrycontext = ac
279 279 def makectx(f, n):
280 280 if n in node.wdirnodes: # in a working context?
281 281 if ctx.rev() is None:
282 282 return ctx.filectx(f)
283 283 return repo[None][f]
284 284 fctx = repo.filectx(f, fileid=n)
285 285 # setup only needed for filectx not create from a changectx
286 286 fctx._ancestrycontext = ac
287 287 fctx._descendantrev = rev
288 288 return fctx
289 289 return util.lrucachefunc(makectx)
290 290
291 291 def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge):
292 292 """combine partial copy paths"""
293 293 remainder = {}
294 294 for f in copyfrom:
295 295 if f in copyto:
296 296 finalcopy[copyto[f]] = copyfrom[f]
297 297 del copyto[f]
298 298 for f in incompletediverge:
299 299 assert f not in diverge
300 300 ic = incompletediverge[f]
301 301 if ic[0] in copyto:
302 302 diverge[f] = [copyto[ic[0]], ic[1]]
303 303 else:
304 304 remainder[f] = ic
305 305 return remainder
306 306
307 307 def mergecopies(repo, c1, c2, base):
308 308 """
309 309 The basic algorithm for copytracing. Copytracing is used in commands like
310 310 rebase, merge, unshelve, etc to merge files that were moved/ copied in one
311 311 merge parent and modified in another. For example:
312 312
313 313 o ---> 4 another commit
314 314 |
315 315 | o ---> 3 commit that modifies a.txt
316 316 | /
317 317 o / ---> 2 commit that moves a.txt to b.txt
318 318 |/
319 319 o ---> 1 merge base
320 320
321 321 If we try to rebase revision 3 on revision 4, since there is no a.txt in
322 322 revision 4, and if user have copytrace disabled, we prints the following
323 323 message:
324 324
325 325 ```other changed <file> which local deleted```
326 326
327 327 If copytrace is enabled, this function finds all the new files that were
328 328 added from merge base up to the top commit (here 4), and for each file it
329 329 checks if this file was copied from another file (a.txt in the above case).
330 330
331 331 Find moves and copies between context c1 and c2 that are relevant
332 332 for merging. 'base' will be used as the merge base.
333 333
334 334 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
335 335 "dirmove".
336 336
337 337 "copy" is a mapping from destination name -> source name,
338 338 where source is in c1 and destination is in c2 or vice-versa.
339 339
340 340 "movewithdir" is a mapping from source name -> destination name,
341 341 where the file at source present in one context but not the other
342 342 needs to be moved to destination by the merge process, because the
343 343 other context moved the directory it is in.
344 344
345 345 "diverge" is a mapping of source name -> list of destination names
346 346 for divergent renames.
347 347
348 348 "renamedelete" is a mapping of source name -> list of destination
349 349 names for files deleted in c1 that were renamed in c2 or vice-versa.
350 350
351 351 "dirmove" is a mapping of detected source dir -> destination dir renames.
352 352 This is needed for handling changes to new files previously grafted into
353 353 renamed directories.
354 354 """
355 355 # avoid silly behavior for update from empty dir
356 356 if not c1 or not c2 or c1 == c2:
357 357 return {}, {}, {}, {}, {}
358 358
359 359 # avoid silly behavior for parent -> working dir
360 360 if c2.node() is None and c1.node() == repo.dirstate.p1():
361 361 return repo.dirstate.copies(), {}, {}, {}, {}
362 362
363 363 # Copy trace disabling is explicitly below the node == p1 logic above
364 364 # because the logic above is required for a simple copy to be kept across a
365 365 # rebase.
366 if repo.ui.configbool('experimental', 'disablecopytrace'):
366 if repo.ui.config('experimental', 'copytrace') == 'off':
367 367 return {}, {}, {}, {}, {}
368 368
369 369 # In certain scenarios (e.g. graft, update or rebase), base can be
370 370 # overridden We still need to know a real common ancestor in this case We
371 371 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
372 372 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
373 373 # caller may not know if the revision passed in lieu of the CA is a genuine
374 374 # common ancestor or not without explicitly checking it, it's better to
375 375 # determine that here.
376 376 #
377 377 # base.descendant(wc) and base.descendant(base) are False, work around that
378 378 _c1 = c1.p1() if c1.rev() is None else c1
379 379 _c2 = c2.p1() if c2.rev() is None else c2
380 380 # an endpoint is "dirty" if it isn't a descendant of the merge base
381 381 # if we have a dirty endpoint, we need to trigger graft logic, and also
382 382 # keep track of which endpoint is dirty
383 383 dirtyc1 = not (base == _c1 or base.descendant(_c1))
384 384 dirtyc2 = not (base == _c2 or base.descendant(_c2))
385 385 graft = dirtyc1 or dirtyc2
386 386 tca = base
387 387 if graft:
388 388 tca = _c1.ancestor(_c2)
389 389
390 390 limit = _findlimit(repo, c1.rev(), c2.rev())
391 391 if limit is None:
392 392 # no common ancestor, no copies
393 393 return {}, {}, {}, {}, {}
394 394 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
395 395
396 396 m1 = c1.manifest()
397 397 m2 = c2.manifest()
398 398 mb = base.manifest()
399 399
400 400 # gather data from _checkcopies:
401 401 # - diverge = record all diverges in this dict
402 402 # - copy = record all non-divergent copies in this dict
403 403 # - fullcopy = record all copies in this dict
404 404 # - incomplete = record non-divergent partial copies here
405 405 # - incompletediverge = record divergent partial copies here
406 406 diverge = {} # divergence data is shared
407 407 incompletediverge = {}
408 408 data1 = {'copy': {},
409 409 'fullcopy': {},
410 410 'incomplete': {},
411 411 'diverge': diverge,
412 412 'incompletediverge': incompletediverge,
413 413 }
414 414 data2 = {'copy': {},
415 415 'fullcopy': {},
416 416 'incomplete': {},
417 417 'diverge': diverge,
418 418 'incompletediverge': incompletediverge,
419 419 }
420 420
421 421 # find interesting file sets from manifests
422 422 addedinm1 = m1.filesnotin(mb)
423 423 addedinm2 = m2.filesnotin(mb)
424 424 bothnew = sorted(addedinm1 & addedinm2)
425 425 if tca == base:
426 426 # unmatched file from base
427 427 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
428 428 u1u, u2u = u1r, u2r
429 429 else:
430 430 # unmatched file from base (DAG rotation in the graft case)
431 431 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
432 432 baselabel='base')
433 433 # unmatched file from topological common ancestors (no DAG rotation)
434 434 # need to recompute this for directory move handling when grafting
435 435 mta = tca.manifest()
436 436 u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
437 437 m2.filesnotin(mta),
438 438 baselabel='topological common ancestor')
439 439
440 440 for f in u1u:
441 441 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)
442 442
443 443 for f in u2u:
444 444 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)
445 445
446 446 copy = dict(data1['copy'])
447 447 copy.update(data2['copy'])
448 448 fullcopy = dict(data1['fullcopy'])
449 449 fullcopy.update(data2['fullcopy'])
450 450
451 451 if dirtyc1:
452 452 _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge,
453 453 incompletediverge)
454 454 else:
455 455 _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge,
456 456 incompletediverge)
457 457
458 458 renamedelete = {}
459 459 renamedeleteset = set()
460 460 divergeset = set()
461 461 for of, fl in diverge.items():
462 462 if len(fl) == 1 or of in c1 or of in c2:
463 463 del diverge[of] # not actually divergent, or not a rename
464 464 if of not in c1 and of not in c2:
465 465 # renamed on one side, deleted on the other side, but filter
466 466 # out files that have been renamed and then deleted
467 467 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
468 468 renamedeleteset.update(fl) # reverse map for below
469 469 else:
470 470 divergeset.update(fl) # reverse map for below
471 471
472 472 if bothnew:
473 473 repo.ui.debug(" unmatched files new in both:\n %s\n"
474 474 % "\n ".join(bothnew))
475 475 bothdiverge = {}
476 476 bothincompletediverge = {}
477 477 remainder = {}
478 478 both1 = {'copy': {},
479 479 'fullcopy': {},
480 480 'incomplete': {},
481 481 'diverge': bothdiverge,
482 482 'incompletediverge': bothincompletediverge
483 483 }
484 484 both2 = {'copy': {},
485 485 'fullcopy': {},
486 486 'incomplete': {},
487 487 'diverge': bothdiverge,
488 488 'incompletediverge': bothincompletediverge
489 489 }
490 490 for f in bothnew:
491 491 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)
492 492 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)
493 493 if dirtyc1:
494 494 # incomplete copies may only be found on the "dirty" side for bothnew
495 495 assert not both2['incomplete']
496 496 remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge,
497 497 bothincompletediverge)
498 498 elif dirtyc2:
499 499 assert not both1['incomplete']
500 500 remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge,
501 501 bothincompletediverge)
502 502 else:
503 503 # incomplete copies and divergences can't happen outside grafts
504 504 assert not both1['incomplete']
505 505 assert not both2['incomplete']
506 506 assert not bothincompletediverge
507 507 for f in remainder:
508 508 assert f not in bothdiverge
509 509 ic = remainder[f]
510 510 if ic[0] in (m1 if dirtyc1 else m2):
511 511 # backed-out rename on one side, but watch out for deleted files
512 512 bothdiverge[f] = ic
513 513 for of, fl in bothdiverge.items():
514 514 if len(fl) == 2 and fl[0] == fl[1]:
515 515 copy[fl[0]] = of # not actually divergent, just matching renames
516 516
517 517 if fullcopy and repo.ui.debugflag:
518 518 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
519 519 "% = renamed and deleted):\n")
520 520 for f in sorted(fullcopy):
521 521 note = ""
522 522 if f in copy:
523 523 note += "*"
524 524 if f in divergeset:
525 525 note += "!"
526 526 if f in renamedeleteset:
527 527 note += "%"
528 528 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
529 529 note))
530 530 del divergeset
531 531
532 532 if not fullcopy:
533 533 return copy, {}, diverge, renamedelete, {}
534 534
535 535 repo.ui.debug(" checking for directory renames\n")
536 536
537 537 # generate a directory move map
538 538 d1, d2 = c1.dirs(), c2.dirs()
539 539 # Hack for adding '', which is not otherwise added, to d1 and d2
540 540 d1.addpath('/')
541 541 d2.addpath('/')
542 542 invalid = set()
543 543 dirmove = {}
544 544
545 545 # examine each file copy for a potential directory move, which is
546 546 # when all the files in a directory are moved to a new directory
547 547 for dst, src in fullcopy.iteritems():
548 548 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
549 549 if dsrc in invalid:
550 550 # already seen to be uninteresting
551 551 continue
552 552 elif dsrc in d1 and ddst in d1:
553 553 # directory wasn't entirely moved locally
554 554 invalid.add(dsrc + "/")
555 555 elif dsrc in d2 and ddst in d2:
556 556 # directory wasn't entirely moved remotely
557 557 invalid.add(dsrc + "/")
558 558 elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/":
559 559 # files from the same directory moved to two different places
560 560 invalid.add(dsrc + "/")
561 561 else:
562 562 # looks good so far
563 563 dirmove[dsrc + "/"] = ddst + "/"
564 564
565 565 for i in invalid:
566 566 if i in dirmove:
567 567 del dirmove[i]
568 568 del d1, d2, invalid
569 569
570 570 if not dirmove:
571 571 return copy, {}, diverge, renamedelete, {}
572 572
573 573 for d in dirmove:
574 574 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
575 575 (d, dirmove[d]))
576 576
577 577 movewithdir = {}
578 578 # check unaccounted nonoverlapping files against directory moves
579 579 for f in u1r + u2r:
580 580 if f not in fullcopy:
581 581 for d in dirmove:
582 582 if f.startswith(d):
583 583 # new file added in a directory that was moved, move it
584 584 df = dirmove[d] + f[len(d):]
585 585 if df not in copy:
586 586 movewithdir[f] = df
587 587 repo.ui.debug((" pending file src: '%s' -> "
588 588 "dst: '%s'\n") % (f, df))
589 589 break
590 590
591 591 return copy, movewithdir, diverge, renamedelete, dirmove
592 592
593 593 def _related(f1, f2, limit):
594 594 """return True if f1 and f2 filectx have a common ancestor
595 595
596 596 Walk back to common ancestor to see if the two files originate
597 597 from the same file. Since workingfilectx's rev() is None it messes
598 598 up the integer comparison logic, hence the pre-step check for
599 599 None (f1 and f2 can only be workingfilectx's initially).
600 600 """
601 601
602 602 if f1 == f2:
603 603 return f1 # a match
604 604
605 605 g1, g2 = f1.ancestors(), f2.ancestors()
606 606 try:
607 607 f1r, f2r = f1.linkrev(), f2.linkrev()
608 608
609 609 if f1r is None:
610 610 f1 = next(g1)
611 611 if f2r is None:
612 612 f2 = next(g2)
613 613
614 614 while True:
615 615 f1r, f2r = f1.linkrev(), f2.linkrev()
616 616 if f1r > f2r:
617 617 f1 = next(g1)
618 618 elif f2r > f1r:
619 619 f2 = next(g2)
620 620 elif f1 == f2:
621 621 return f1 # a match
622 622 elif f1r == f2r or f1r < limit or f2r < limit:
623 623 return False # copy no longer relevant
624 624 except StopIteration:
625 625 return False
626 626
627 627 def _checkcopies(srcctx, dstctx, f, base, tca, remotebase, limit, data):
628 628 """
629 629 check possible copies of f from msrc to mdst
630 630
631 631 srcctx = starting context for f in msrc
632 632 dstctx = destination context for f in mdst
633 633 f = the filename to check (as in msrc)
634 634 base = the changectx used as a merge base
635 635 tca = topological common ancestor for graft-like scenarios
636 636 remotebase = True if base is outside tca::srcctx, False otherwise
637 637 limit = the rev number to not search beyond
638 638 data = dictionary of dictionary to store copy data. (see mergecopies)
639 639
640 640 note: limit is only an optimization, and provides no guarantee that
641 641 irrelevant revisions will not be visited
642 642 there is no easy way to make this algorithm stop in a guaranteed way
643 643 once it "goes behind a certain revision".
644 644 """
645 645
646 646 msrc = srcctx.manifest()
647 647 mdst = dstctx.manifest()
648 648 mb = base.manifest()
649 649 mta = tca.manifest()
650 650 # Might be true if this call is about finding backward renames,
651 651 # This happens in the case of grafts because the DAG is then rotated.
652 652 # If the file exists in both the base and the source, we are not looking
653 653 # for a rename on the source side, but on the part of the DAG that is
654 654 # traversed backwards.
655 655 #
656 656 # In the case there is both backward and forward renames (before and after
657 657 # the base) this is more complicated as we must detect a divergence.
658 658 # We use 'backwards = False' in that case.
659 659 backwards = not remotebase and base != tca and f in mb
660 660 getsrcfctx = _makegetfctx(srcctx)
661 661 getdstfctx = _makegetfctx(dstctx)
662 662
663 663 if msrc[f] == mb.get(f) and not remotebase:
664 664 # Nothing to merge
665 665 return
666 666
667 667 of = None
668 668 seen = {f}
669 669 for oc in getsrcfctx(f, msrc[f]).ancestors():
670 670 ocr = oc.linkrev()
671 671 of = oc.path()
672 672 if of in seen:
673 673 # check limit late - grab last rename before
674 674 if ocr < limit:
675 675 break
676 676 continue
677 677 seen.add(of)
678 678
679 679 # remember for dir rename detection
680 680 if backwards:
681 681 data['fullcopy'][of] = f # grafting backwards through renames
682 682 else:
683 683 data['fullcopy'][f] = of
684 684 if of not in mdst:
685 685 continue # no match, keep looking
686 686 if mdst[of] == mb.get(of):
687 687 return # no merge needed, quit early
688 688 c2 = getdstfctx(of, mdst[of])
689 689 # c2 might be a plain new file on added on destination side that is
690 690 # unrelated to the droids we are looking for.
691 691 cr = _related(oc, c2, tca.rev())
692 692 if cr and (of == f or of == c2.path()): # non-divergent
693 693 if backwards:
694 694 data['copy'][of] = f
695 695 elif of in mb:
696 696 data['copy'][f] = of
697 697 elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename
698 698 data['copy'][of] = f
699 699 del data['fullcopy'][f]
700 700 data['fullcopy'][of] = f
701 701 else: # divergence w.r.t. graft CA on one side of topological CA
702 702 for sf in seen:
703 703 if sf in mb:
704 704 assert sf not in data['diverge']
705 705 data['diverge'][sf] = [f, of]
706 706 break
707 707 return
708 708
709 709 if of in mta:
710 710 if backwards or remotebase:
711 711 data['incomplete'][of] = f
712 712 else:
713 713 for sf in seen:
714 714 if sf in mb:
715 715 if tca == base:
716 716 data['diverge'].setdefault(sf, []).append(f)
717 717 else:
718 718 data['incompletediverge'][sf] = [of, f]
719 719 return
720 720
721 721 def duplicatecopies(repo, rev, fromrev, skiprev=None):
722 722 '''reproduce copies from fromrev to rev in the dirstate
723 723
724 724 If skiprev is specified, it's a revision that should be used to
725 725 filter copy records. Any copies that occur between fromrev and
726 726 skiprev will not be duplicated, even if they appear in the set of
727 727 copies between fromrev and rev.
728 728 '''
729 729 exclude = {}
730 730 if (skiprev is not None and
731 not repo.ui.configbool('experimental', 'disablecopytrace')):
732 # disablecopytrace skips this line, but not the entire function because
731 repo.ui.config('experimental', 'copytrace') != 'off'):
732 # copytrace='off' skips this line, but not the entire function because
733 733 # the line below is O(size of the repo) during a rebase, while the rest
734 734 # of the function is much faster (and is required for carrying copy
735 735 # metadata across the rebase anyway).
736 736 exclude = pathcopies(repo[fromrev], repo[skiprev])
737 737 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
738 738 # copies.pathcopies returns backward renames, so dst might not
739 739 # actually be in the dirstate
740 740 if dst in exclude:
741 741 continue
742 742 if repo.dirstate[dst] in "nma":
743 743 repo.dirstate.copy(src, dst)
@@ -1,168 +1,168 b''
1 1 $ hg init t
2 2 $ cd t
3 3
4 4 $ echo 1 > a
5 5 $ hg ci -qAm "first"
6 6
7 7 $ hg cp a b
8 8 $ hg mv a c
9 9 $ echo 2 >> b
10 10 $ echo 2 >> c
11 11
12 12 $ hg ci -qAm "second"
13 13
14 14 $ hg co -C 0
15 15 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
16 16
17 17 $ echo 0 > a
18 18 $ echo 1 >> a
19 19
20 20 $ hg ci -qAm "other"
21 21
22 22 $ hg merge --debug
23 23 searching for copies back to rev 1
24 24 unmatched files in other:
25 25 b
26 26 c
27 27 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
28 28 src: 'a' -> dst: 'b' *
29 29 src: 'a' -> dst: 'c' *
30 30 checking for directory renames
31 31 resolving manifests
32 32 branchmerge: True, force: False, partial: False
33 33 ancestor: b8bf91eeebbc, local: add3f11052fa+, remote: 17c05bb7fcb6
34 34 preserving a for resolve of b
35 35 preserving a for resolve of c
36 36 removing a
37 37 starting 4 threads for background file closing (?)
38 38 b: remote moved from a -> m (premerge)
39 39 picked tool ':merge' for b (binary False symlink False changedelete False)
40 40 merging a and b to b
41 41 my b@add3f11052fa+ other b@17c05bb7fcb6 ancestor a@b8bf91eeebbc
42 42 premerge successful
43 43 c: remote moved from a -> m (premerge)
44 44 picked tool ':merge' for c (binary False symlink False changedelete False)
45 45 merging a and c to c
46 46 my c@add3f11052fa+ other c@17c05bb7fcb6 ancestor a@b8bf91eeebbc
47 47 premerge successful
48 48 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
49 49 (branch merge, don't forget to commit)
50 50
51 51 file b
52 52 $ cat b
53 53 0
54 54 1
55 55 2
56 56
57 57 file c
58 58 $ cat c
59 59 0
60 60 1
61 61 2
62 62
63 63 Test disabling copy tracing
64 64
65 65 - first verify copy metadata was kept
66 66
67 67 $ hg up -qC 2
68 68 $ hg rebase --keep -d 1 -b 2 --config extensions.rebase=
69 69 rebasing 2:add3f11052fa "other" (tip)
70 70 merging b and a to b
71 71 merging c and a to c
72 72
73 73 $ cat b
74 74 0
75 75 1
76 76 2
77 77
78 78 - next verify copy metadata is lost when disabled
79 79
80 80 $ hg strip -r . --config extensions.strip=
81 81 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 82 saved backup bundle to $TESTTMP/t/.hg/strip-backup/550bd84c0cd3-fc575957-backup.hg (glob)
83 83 $ hg up -qC 2
84 $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True --config ui.interactive=True << EOF
84 $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= --config experimental.copytrace=off --config ui.interactive=True << EOF
85 85 > c
86 86 > EOF
87 87 rebasing 2:add3f11052fa "other" (tip)
88 88 other [source] changed a which local [dest] deleted
89 89 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
90 90
91 91 $ cat b
92 92 1
93 93 2
94 94
95 95 $ cd ..
96 96
97 97 Verify disabling copy tracing still keeps copies from rebase source
98 98
99 99 $ hg init copydisable
100 100 $ cd copydisable
101 101 $ touch a
102 102 $ hg ci -Aqm 'add a'
103 103 $ touch b
104 104 $ hg ci -Aqm 'add b, c'
105 105 $ hg cp b x
106 106 $ echo x >> x
107 107 $ hg ci -qm 'copy b->x'
108 108 $ hg up -q 1
109 109 $ touch z
110 110 $ hg ci -Aqm 'add z'
111 111 $ hg log -G -T '{rev} {desc}\n'
112 112 @ 3 add z
113 113 |
114 114 | o 2 copy b->x
115 115 |/
116 116 o 1 add b, c
117 117 |
118 118 o 0 add a
119 119
120 $ hg rebase -d . -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True
120 $ hg rebase -d . -b 2 --config extensions.rebase= --config experimental.copytrace=off
121 121 rebasing 2:6adcf8c12e7d "copy b->x"
122 122 saved backup bundle to $TESTTMP/copydisable/.hg/strip-backup/6adcf8c12e7d-ce4b3e75-rebase.hg (glob)
123 123 $ hg up -q 3
124 124 $ hg log -f x -T '{rev} {desc}\n'
125 125 3 copy b->x
126 126 1 add b, c
127 127
128 128 $ cd ../
129 129
130 130 Verify we duplicate existing copies, instead of detecting them
131 131
132 132 $ hg init copydisable3
133 133 $ cd copydisable3
134 134 $ touch a
135 135 $ hg ci -Aqm 'add a'
136 136 $ hg cp a b
137 137 $ hg ci -Aqm 'copy a->b'
138 138 $ hg mv b c
139 139 $ hg ci -Aqm 'move b->c'
140 140 $ hg up -q 0
141 141 $ hg cp a b
142 142 $ echo b >> b
143 143 $ hg ci -Aqm 'copy a->b (2)'
144 144 $ hg log -G -T '{rev} {desc}\n'
145 145 @ 3 copy a->b (2)
146 146 |
147 147 | o 2 move b->c
148 148 | |
149 149 | o 1 copy a->b
150 150 |/
151 151 o 0 add a
152 152
153 $ hg rebase -d 2 -s 3 --config extensions.rebase= --config experimental.disablecopytrace=True
153 $ hg rebase -d 2 -s 3 --config extensions.rebase= --config experimental.copytrace=off
154 154 rebasing 3:47e1a9e6273b "copy a->b (2)" (tip)
155 155 saved backup bundle to $TESTTMP/copydisable3/.hg/strip-backup/47e1a9e6273b-2d099c59-rebase.hg (glob)
156 156
157 157 $ hg log -G -f b
158 158 @ changeset: 3:76024fb4b05b
159 159 : tag: tip
160 160 : user: test
161 161 : date: Thu Jan 01 00:00:00 1970 +0000
162 162 : summary: copy a->b (2)
163 163 :
164 164 o changeset: 0:ac82d8b1f7c4
165 165 user: test
166 166 date: Thu Jan 01 00:00:00 1970 +0000
167 167 summary: add a
168 168
General Comments 0
You need to be logged in to leave comments. Login now