comparison mercurial/copies.py @ 44200:fa9ad1da2e77

merge: start using the per-side copy dicts The point of this patch is mostly to clarify `manifestmerge()`. I find it much easier to reason about now. Differential Revision: https://phab.mercurial-scm.org/D7990
author Martin von Zweigbergk <martinvonz@google.com>
date Thu, 23 Jan 2020 15:44:30 -0800
parents 7f8bdee0034e
children d0c3eead515a
comparison
equal deleted inserted replaced
44199:7f8bdee0034e 44200:fa9ad1da2e77
461 461
462 This function calls different copytracing algorithms based on config. 462 This function calls different copytracing algorithms based on config.
463 """ 463 """
464 # avoid silly behavior for update from empty dir 464 # avoid silly behavior for update from empty dir
465 if not c1 or not c2 or c1 == c2: 465 if not c1 or not c2 or c1 == c2:
466 return branch_copies(), {} 466 return branch_copies(), branch_copies(), {}
467 467
468 narrowmatch = c1.repo().narrowmatch() 468 narrowmatch = c1.repo().narrowmatch()
469 469
470 # avoid silly behavior for parent -> working dir 470 # avoid silly behavior for parent -> working dir
471 if c2.node() is None and c1.node() == repo.dirstate.p1(): 471 if c2.node() is None and c1.node() == repo.dirstate.p1():
472 return branch_copies(_dirstatecopies(repo, narrowmatch)), {} 472 return (
473 branch_copies(_dirstatecopies(repo, narrowmatch)),
474 branch_copies(),
475 {},
476 )
473 477
474 copytracing = repo.ui.config(b'experimental', b'copytrace') 478 copytracing = repo.ui.config(b'experimental', b'copytrace')
475 if stringutil.parsebool(copytracing) is False: 479 if stringutil.parsebool(copytracing) is False:
476 # stringutil.parsebool() returns None when it is unable to parse the 480 # stringutil.parsebool() returns None when it is unable to parse the
477 # value, so we should rely on making sure copytracing is on such cases 481 # value, so we should rely on making sure copytracing is on such cases
478 return branch_copies(), {} 482 return branch_copies(), branch_copies(), {}
479 483
480 if usechangesetcentricalgo(repo): 484 if usechangesetcentricalgo(repo):
481 # The heuristics don't make sense when we need changeset-centric algos 485 # The heuristics don't make sense when we need changeset-centric algos
482 return _fullcopytracing(repo, c1, c2, base) 486 return _fullcopytracing(repo, c1, c2, base)
483 487
576 580
577 copies1 = pathcopies(base, c1) 581 copies1 = pathcopies(base, c1)
578 copies2 = pathcopies(base, c2) 582 copies2 = pathcopies(base, c2)
579 583
580 if not (copies1 or copies2): 584 if not (copies1 or copies2):
581 return branch_copies(), {} 585 return branch_copies(), branch_copies(), {}
582 586
583 inversecopies1 = {} 587 inversecopies1 = {}
584 inversecopies2 = {} 588 inversecopies2 = {}
585 for dst, src in copies1.items(): 589 for dst, src in copies1.items():
586 inversecopies1.setdefault(src, []).append(dst) 590 inversecopies1.setdefault(src, []).append(dst)
679 repo.ui.debug(b" checking for directory renames\n") 683 repo.ui.debug(b" checking for directory renames\n")
680 684
681 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2) 685 dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
682 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1) 686 dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
683 687
684 copy1.update(copy2) 688 branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
685 renamedelete1.update(renamedelete2) 689 branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
686 movewithdir1.update(movewithdir2) 690
687 dirmove1.update(dirmove2) 691 return branch_copies1, branch_copies2, diverge
688
689 return branch_copies(copy1, renamedelete1, dirmove1, movewithdir1), diverge
690 692
691 693
692 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles): 694 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
693 """Finds moved directories and files that should move with them. 695 """Finds moved directories and files that should move with them.
694 696
782 if c1.rev() is None: 784 if c1.rev() is None:
783 c1 = c1.p1() 785 c1 = c1.p1()
784 if c2.rev() is None: 786 if c2.rev() is None:
785 c2 = c2.p1() 787 c2 = c2.p1()
786 788
787 copies = {}
788
789 changedfiles = set() 789 changedfiles = set()
790 m1 = c1.manifest() 790 m1 = c1.manifest()
791 if not repo.revs(b'%d::%d', base.rev(), c2.rev()): 791 if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
792 # If base is not in c2 branch, we switch to fullcopytracing 792 # If base is not in c2 branch, we switch to fullcopytracing
793 repo.ui.debug( 793 repo.ui.debug(
803 repo.ui.debug(b"switching to full copytracing because of merges\n") 803 repo.ui.debug(b"switching to full copytracing because of merges\n")
804 return _fullcopytracing(repo, c1, c2, base) 804 return _fullcopytracing(repo, c1, c2, base)
805 changedfiles.update(ctx.files()) 805 changedfiles.update(ctx.files())
806 ctx = ctx.p1() 806 ctx = ctx.p1()
807 807
808 copies2 = {}
808 cp = _forwardcopies(base, c2) 809 cp = _forwardcopies(base, c2)
809 for dst, src in pycompat.iteritems(cp): 810 for dst, src in pycompat.iteritems(cp):
810 if src in m1: 811 if src in m1:
811 copies[dst] = src 812 copies2[dst] = src
812 813
813 # file is missing if it isn't present in the destination, but is present in 814 # file is missing if it isn't present in the destination, but is present in
814 # the base and present in the source. 815 # the base and present in the source.
815 # Presence in the base is important to exclude added files, presence in the 816 # Presence in the base is important to exclude added files, presence in the
816 # source is important to exclude removed files. 817 # source is important to exclude removed files.
817 filt = lambda f: f not in m1 and f in base and f in c2 818 filt = lambda f: f not in m1 and f in base and f in c2
818 missingfiles = [f for f in changedfiles if filt(f)] 819 missingfiles = [f for f in changedfiles if filt(f)]
819 820
821 copies1 = {}
820 if missingfiles: 822 if missingfiles:
821 basenametofilename = collections.defaultdict(list) 823 basenametofilename = collections.defaultdict(list)
822 dirnametofilename = collections.defaultdict(list) 824 dirnametofilename = collections.defaultdict(list)
823 825
824 for f in m1.filesnotin(base.manifest()): 826 for f in m1.filesnotin(base.manifest()):
856 f1 = c1.filectx(candidate) 858 f1 = c1.filectx(candidate)
857 if _related(f1, f2): 859 if _related(f1, f2):
858 # if there are a few related copies then we'll merge 860 # if there are a few related copies then we'll merge
859 # changes into all of them. This matches the behaviour 861 # changes into all of them. This matches the behaviour
860 # of upstream copytracing 862 # of upstream copytracing
861 copies[candidate] = f 863 copies1[candidate] = f
862 864
863 return branch_copies(copies), {} 865 return branch_copies(copies1), branch_copies(copies2), {}
864 866
865 867
866 def _related(f1, f2): 868 def _related(f1, f2):
867 """return True if f1 and f2 filectx have a common ancestor 869 """return True if f1 and f2 filectx have a common ancestor
868 870