comparison mercurial/copies.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children d783f945a701
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
168 168
169 def _dirstatecopies(repo, match=None): 169 def _dirstatecopies(repo, match=None):
170 ds = repo.dirstate 170 ds = repo.dirstate
171 c = ds.copies().copy() 171 c = ds.copies().copy()
172 for k in list(c): 172 for k in list(c):
173 if ds[k] not in 'anm' or (match and not match(k)): 173 if ds[k] not in b'anm' or (match and not match(k)):
174 del c[k] 174 del c[k]
175 return c 175 return c
176 176
177 177
178 def _computeforwardmissing(a, b, match=None): 178 def _computeforwardmissing(a, b, match=None):
185 return mb.filesnotin(ma, match=match) 185 return mb.filesnotin(ma, match=match)
186 186
187 187
188 def usechangesetcentricalgo(repo): 188 def usechangesetcentricalgo(repo):
189 """Checks if we should use changeset-centric copy algorithms""" 189 """Checks if we should use changeset-centric copy algorithms"""
190 readfrom = repo.ui.config('experimental', 'copies.read-from') 190 readfrom = repo.ui.config(b'experimental', b'copies.read-from')
191 changesetsource = ('changeset-only', 'compatibility') 191 changesetsource = (b'changeset-only', b'compatibility')
192 return readfrom in changesetsource 192 return readfrom in changesetsource
193 193
194 194
195 def _committedforwardcopies(a, b, base, match): 195 def _committedforwardcopies(a, b, base, match):
196 """Like _forwardcopies(), but b.rev() cannot be None (working copy)""" 196 """Like _forwardcopies(), but b.rev() cannot be None (working copy)"""
199 repo = a._repo 199 repo = a._repo
200 200
201 if usechangesetcentricalgo(repo): 201 if usechangesetcentricalgo(repo):
202 return _changesetforwardcopies(a, b, match) 202 return _changesetforwardcopies(a, b, match)
203 203
204 debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies') 204 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
205 dbg = repo.ui.debug 205 dbg = repo.ui.debug
206 if debug: 206 if debug:
207 dbg('debug.copies: looking into rename from %s to %s\n' % (a, b)) 207 dbg(b'debug.copies: looking into rename from %s to %s\n' % (a, b))
208 limit = _findlimit(repo, a, b) 208 limit = _findlimit(repo, a, b)
209 if debug: 209 if debug:
210 dbg('debug.copies: search limit: %d\n' % limit) 210 dbg(b'debug.copies: search limit: %d\n' % limit)
211 am = a.manifest() 211 am = a.manifest()
212 basemf = None if base is None else base.manifest() 212 basemf = None if base is None else base.manifest()
213 213
214 # find where new files came from 214 # find where new files came from
215 # we currently don't try to find where old files went, too expensive 215 # we currently don't try to find where old files went, too expensive
229 missing = _computeforwardmissing(a, b, match=forwardmissingmatch) 229 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
230 230
231 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True) 231 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
232 232
233 if debug: 233 if debug:
234 dbg('debug.copies: missing files to search: %d\n' % len(missing)) 234 dbg(b'debug.copies: missing files to search: %d\n' % len(missing))
235 235
236 for f in sorted(missing): 236 for f in sorted(missing):
237 if debug: 237 if debug:
238 dbg('debug.copies: tracing file: %s\n' % f) 238 dbg(b'debug.copies: tracing file: %s\n' % f)
239 fctx = b[f] 239 fctx = b[f]
240 fctx._ancestrycontext = ancestrycontext 240 fctx._ancestrycontext = ancestrycontext
241 241
242 if debug: 242 if debug:
243 start = util.timer() 243 start = util.timer()
244 opath = _tracefile(fctx, am, basemf, limit) 244 opath = _tracefile(fctx, am, basemf, limit)
245 if opath: 245 if opath:
246 if debug: 246 if debug:
247 dbg('debug.copies: rename of: %s\n' % opath) 247 dbg(b'debug.copies: rename of: %s\n' % opath)
248 cm[f] = opath 248 cm[f] = opath
249 if debug: 249 if debug:
250 dbg( 250 dbg(
251 'debug.copies: time: %f seconds\n' 251 b'debug.copies: time: %f seconds\n'
252 % (util.timer() - start) 252 % (util.timer() - start)
253 ) 253 )
254 return cm 254 return cm
255 255
256 256
340 copies = _committedforwardcopies(a, b, base, match) 340 copies = _committedforwardcopies(a, b, base, match)
341 return copies 341 return copies
342 342
343 343
344 def _backwardrenames(a, b, match): 344 def _backwardrenames(a, b, match):
345 if a._repo.ui.config('experimental', 'copytrace') == 'off': 345 if a._repo.ui.config(b'experimental', b'copytrace') == b'off':
346 return {} 346 return {}
347 347
348 # Even though we're not taking copies into account, 1:n rename situations 348 # Even though we're not taking copies into account, 1:n rename situations
349 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we 349 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
350 # arbitrarily pick one of the renames. 350 # arbitrarily pick one of the renames.
364 364
365 365
366 def pathcopies(x, y, match=None): 366 def pathcopies(x, y, match=None):
367 """find {dst@y: src@x} copy mapping for directed compare""" 367 """find {dst@y: src@x} copy mapping for directed compare"""
368 repo = x._repo 368 repo = x._repo
369 debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies') 369 debug = repo.ui.debugflag and repo.ui.configbool(b'devel', b'debug.copies')
370 if debug: 370 if debug:
371 repo.ui.debug('debug.copies: searching copies from %s to %s\n' % (x, y)) 371 repo.ui.debug(
372 b'debug.copies: searching copies from %s to %s\n' % (x, y)
373 )
372 if x == y or not x or not y: 374 if x == y or not x or not y:
373 return {} 375 return {}
374 a = y.ancestor(x) 376 a = y.ancestor(x)
375 if a == x: 377 if a == x:
376 if debug: 378 if debug:
377 repo.ui.debug('debug.copies: search mode: forward\n') 379 repo.ui.debug(b'debug.copies: search mode: forward\n')
378 if y.rev() is None and x == y.p1(): 380 if y.rev() is None and x == y.p1():
379 # short-circuit to avoid issues with merge states 381 # short-circuit to avoid issues with merge states
380 return _dirstatecopies(repo, match) 382 return _dirstatecopies(repo, match)
381 copies = _forwardcopies(x, y, match=match) 383 copies = _forwardcopies(x, y, match=match)
382 elif a == y: 384 elif a == y:
383 if debug: 385 if debug:
384 repo.ui.debug('debug.copies: search mode: backward\n') 386 repo.ui.debug(b'debug.copies: search mode: backward\n')
385 copies = _backwardrenames(x, y, match=match) 387 copies = _backwardrenames(x, y, match=match)
386 else: 388 else:
387 if debug: 389 if debug:
388 repo.ui.debug('debug.copies: search mode: combined\n') 390 repo.ui.debug(b'debug.copies: search mode: combined\n')
389 base = None 391 base = None
390 if a.rev() != node.nullrev: 392 if a.rev() != node.nullrev:
391 base = x 393 base = x
392 copies = _chain( 394 copies = _chain(
393 _backwardrenames(x, a, match=match), 395 _backwardrenames(x, a, match=match),
451 453
452 # avoid silly behavior for parent -> working dir 454 # avoid silly behavior for parent -> working dir
453 if c2.node() is None and c1.node() == repo.dirstate.p1(): 455 if c2.node() is None and c1.node() == repo.dirstate.p1():
454 return _dirstatecopies(repo, narrowmatch), {}, {}, {}, {} 456 return _dirstatecopies(repo, narrowmatch), {}, {}, {}, {}
455 457
456 copytracing = repo.ui.config('experimental', 'copytrace') 458 copytracing = repo.ui.config(b'experimental', b'copytrace')
457 if stringutil.parsebool(copytracing) is False: 459 if stringutil.parsebool(copytracing) is False:
458 # stringutil.parsebool() returns None when it is unable to parse the 460 # stringutil.parsebool() returns None when it is unable to parse the
459 # value, so we should rely on making sure copytracing is on such cases 461 # value, so we should rely on making sure copytracing is on such cases
460 return {}, {}, {}, {}, {} 462 return {}, {}, {}, {}, {}
461 463
464 return _fullcopytracing(repo, c1, c2, base) 466 return _fullcopytracing(repo, c1, c2, base)
465 467
466 # Copy trace disabling is explicitly below the node == p1 logic above 468 # Copy trace disabling is explicitly below the node == p1 logic above
467 # because the logic above is required for a simple copy to be kept across a 469 # because the logic above is required for a simple copy to be kept across a
468 # rebase. 470 # rebase.
469 if copytracing == 'heuristics': 471 if copytracing == b'heuristics':
470 # Do full copytracing if only non-public revisions are involved as 472 # Do full copytracing if only non-public revisions are involved as
471 # that will be fast enough and will also cover the copies which could 473 # that will be fast enough and will also cover the copies which could
472 # be missed by heuristics 474 # be missed by heuristics
473 if _isfullcopytraceable(repo, c1, base): 475 if _isfullcopytraceable(repo, c1, base):
474 return _fullcopytracing(repo, c1, c2, base) 476 return _fullcopytracing(repo, c1, c2, base)
488 """ 490 """
489 if c1.rev() is None: 491 if c1.rev() is None:
490 c1 = c1.p1() 492 c1 = c1.p1()
491 if c1.mutable() and base.mutable(): 493 if c1.mutable() and base.mutable():
492 sourcecommitlimit = repo.ui.configint( 494 sourcecommitlimit = repo.ui.configint(
493 'experimental', 'copytrace.sourcecommitlimit' 495 b'experimental', b'copytrace.sourcecommitlimit'
494 ) 496 )
495 commits = len(repo.revs('%d::%d', base.rev(), c1.rev())) 497 commits = len(repo.revs(b'%d::%d', base.rev(), c1.rev()))
496 return commits < sourcecommitlimit 498 return commits < sourcecommitlimit
497 return False 499 return False
498 500
499 501
500 def _checksinglesidecopies( 502 def _checksinglesidecopies(
590 addedinm1 = m1.filesnotin(mb, repo.narrowmatch()) 592 addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
591 addedinm2 = m2.filesnotin(mb, repo.narrowmatch()) 593 addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
592 u1 = sorted(addedinm1 - addedinm2) 594 u1 = sorted(addedinm1 - addedinm2)
593 u2 = sorted(addedinm2 - addedinm1) 595 u2 = sorted(addedinm2 - addedinm1)
594 596
595 header = " unmatched files in %s" 597 header = b" unmatched files in %s"
596 if u1: 598 if u1:
597 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1))) 599 repo.ui.debug(b"%s:\n %s\n" % (header % b'local', b"\n ".join(u1)))
598 if u2: 600 if u2:
599 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2))) 601 repo.ui.debug(b"%s:\n %s\n" % (header % b'other', b"\n ".join(u2)))
600 602
601 fullcopy = copies1.copy() 603 fullcopy = copies1.copy()
602 fullcopy.update(copies2) 604 fullcopy.update(copies2)
603 if not fullcopy: 605 if not fullcopy:
604 return copy, {}, diverge, renamedelete, {} 606 return copy, {}, diverge, renamedelete, {}
605 607
606 if repo.ui.debugflag: 608 if repo.ui.debugflag:
607 repo.ui.debug( 609 repo.ui.debug(
608 " all copies found (* = to merge, ! = divergent, " 610 b" all copies found (* = to merge, ! = divergent, "
609 "% = renamed and deleted):\n" 611 b"% = renamed and deleted):\n"
610 ) 612 )
611 for f in sorted(fullcopy): 613 for f in sorted(fullcopy):
612 note = "" 614 note = b""
613 if f in copy: 615 if f in copy:
614 note += "*" 616 note += b"*"
615 if f in divergeset: 617 if f in divergeset:
616 note += "!" 618 note += b"!"
617 if f in renamedeleteset: 619 if f in renamedeleteset:
618 note += "%" 620 note += b"%"
619 repo.ui.debug( 621 repo.ui.debug(
620 " src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, note) 622 b" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, note)
621 ) 623 )
622 del divergeset 624 del divergeset
623 625
624 repo.ui.debug(" checking for directory renames\n") 626 repo.ui.debug(b" checking for directory renames\n")
625 627
626 # generate a directory move map 628 # generate a directory move map
627 d1, d2 = c1.dirs(), c2.dirs() 629 d1, d2 = c1.dirs(), c2.dirs()
628 invalid = set() 630 invalid = set()
629 dirmove = {} 631 dirmove = {}
654 del d1, d2, invalid 656 del d1, d2, invalid
655 657
656 if not dirmove: 658 if not dirmove:
657 return copy, {}, diverge, renamedelete, {} 659 return copy, {}, diverge, renamedelete, {}
658 660
659 dirmove = {k + "/": v + "/" for k, v in dirmove.iteritems()} 661 dirmove = {k + b"/": v + b"/" for k, v in dirmove.iteritems()}
660 662
661 for d in dirmove: 663 for d in dirmove:
662 repo.ui.debug( 664 repo.ui.debug(
663 " discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d]) 665 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
664 ) 666 )
665 667
666 movewithdir = {} 668 movewithdir = {}
667 # check unaccounted nonoverlapping files against directory moves 669 # check unaccounted nonoverlapping files against directory moves
668 for f in u1 + u2: 670 for f in u1 + u2:
672 # new file added in a directory that was moved, move it 674 # new file added in a directory that was moved, move it
673 df = dirmove[d] + f[len(d) :] 675 df = dirmove[d] + f[len(d) :]
674 if df not in copy: 676 if df not in copy:
675 movewithdir[f] = df 677 movewithdir[f] = df
676 repo.ui.debug( 678 repo.ui.debug(
677 (" pending file src: '%s' -> " "dst: '%s'\n") 679 (b" pending file src: '%s' -> " b"dst: '%s'\n")
678 % (f, df) 680 % (f, df)
679 ) 681 )
680 break 682 break
681 683
682 return copy, movewithdir, diverge, renamedelete, dirmove 684 return copy, movewithdir, diverge, renamedelete, dirmove
714 716
715 copies = {} 717 copies = {}
716 718
717 changedfiles = set() 719 changedfiles = set()
718 m1 = c1.manifest() 720 m1 = c1.manifest()
719 if not repo.revs('%d::%d', base.rev(), c2.rev()): 721 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
720 # If base is not in c2 branch, we switch to fullcopytracing 722 # If base is not in c2 branch, we switch to fullcopytracing
721 repo.ui.debug( 723 repo.ui.debug(
722 "switching to full copytracing as base is not " 724 b"switching to full copytracing as base is not "
723 "an ancestor of c2\n" 725 b"an ancestor of c2\n"
724 ) 726 )
725 return _fullcopytracing(repo, c1, c2, base) 727 return _fullcopytracing(repo, c1, c2, base)
726 728
727 ctx = c2 729 ctx = c2
728 while ctx != base: 730 while ctx != base:
729 if len(ctx.parents()) == 2: 731 if len(ctx.parents()) == 2:
730 # To keep things simple let's not handle merges 732 # To keep things simple let's not handle merges
731 repo.ui.debug("switching to full copytracing because of merges\n") 733 repo.ui.debug(b"switching to full copytracing because of merges\n")
732 return _fullcopytracing(repo, c1, c2, base) 734 return _fullcopytracing(repo, c1, c2, base)
733 changedfiles.update(ctx.files()) 735 changedfiles.update(ctx.files())
734 ctx = ctx.p1() 736 ctx = ctx.p1()
735 737
736 cp = _forwardcopies(base, c2) 738 cp = _forwardcopies(base, c2)
765 # c2.filectx(f) won't fail 767 # c2.filectx(f) won't fail
766 f2 = c2.filectx(f) 768 f2 = c2.filectx(f)
767 # we can have a lot of candidates which can slow down the heuristics 769 # we can have a lot of candidates which can slow down the heuristics
768 # config value to limit the number of candidates moves to check 770 # config value to limit the number of candidates moves to check
769 maxcandidates = repo.ui.configint( 771 maxcandidates = repo.ui.configint(
770 'experimental', 'copytrace.movecandidateslimit' 772 b'experimental', b'copytrace.movecandidateslimit'
771 ) 773 )
772 774
773 if len(movecandidates) > maxcandidates: 775 if len(movecandidates) > maxcandidates:
774 repo.ui.status( 776 repo.ui.status(
775 _( 777 _(
776 "skipping copytracing for '%s', more " 778 b"skipping copytracing for '%s', more "
777 "candidates than the limit: %d\n" 779 b"candidates than the limit: %d\n"
778 ) 780 )
779 % (f, len(movecandidates)) 781 % (f, len(movecandidates))
780 ) 782 )
781 continue 783 continue
782 784
831 filter copy records. Any copies that occur between fromrev and 833 filter copy records. Any copies that occur between fromrev and
832 skiprev will not be duplicated, even if they appear in the set of 834 skiprev will not be duplicated, even if they appear in the set of
833 copies between fromrev and rev. 835 copies between fromrev and rev.
834 """ 836 """
835 exclude = {} 837 exclude = {}
836 ctraceconfig = repo.ui.config('experimental', 'copytrace') 838 ctraceconfig = repo.ui.config(b'experimental', b'copytrace')
837 bctrace = stringutil.parsebool(ctraceconfig) 839 bctrace = stringutil.parsebool(ctraceconfig)
838 if skiprev is not None and ( 840 if skiprev is not None and (
839 ctraceconfig == 'heuristics' or bctrace or bctrace is None 841 ctraceconfig == b'heuristics' or bctrace or bctrace is None
840 ): 842 ):
841 # copytrace='off' skips this line, but not the entire function because 843 # copytrace='off' skips this line, but not the entire function because
842 # the line below is O(size of the repo) during a rebase, while the rest 844 # the line below is O(size of the repo) during a rebase, while the rest
843 # of the function is much faster (and is required for carrying copy 845 # of the function is much faster (and is required for carrying copy
844 # metadata across the rebase anyway). 846 # metadata across the rebase anyway).