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 |
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 |