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