Mercurial > public > mercurial-scm > hg
comparison mercurial/merge.py @ 45274:0e18861f96ab
merge: return a mergeresult obj from manifestmerge(), calculateupdates() (API)
Earlier, manifestmerge() and calculateupdates() returns a tuple of three things.
I wanted to add one more thing to return value.
Introducing a special class which represents results of a merge will help
understand better and also ease adding new return values.
Differential Revision: https://phab.mercurial-scm.org/D8799
author | Pulkit Goyal <7895pulkit@gmail.com> |
---|---|
date | Thu, 23 Jul 2020 18:03:14 +0530 |
parents | 4f71d1a99e45 |
children | 8e8d513941b4 |
comparison
equal
deleted
inserted
replaced
45270:c8655782ef19 | 45274:0e18861f96ab |
---|---|
538 raise error.Abort( | 538 raise error.Abort( |
539 _(b'conflict in file \'%s\' is outside narrow clone') % f | 539 _(b'conflict in file \'%s\' is outside narrow clone') % f |
540 ) | 540 ) |
541 | 541 |
542 | 542 |
543 class mergeresult(object): | |
544 ''''An object representing result of merging manifests. | |
545 | |
546 It has information about what actions need to be performed on dirstate | |
547 mapping of divergent renames and other such cases. ''' | |
548 | |
549 def __init__(self, actions, diverge, renamedelete): | |
550 """ | |
551 actions: dict of filename as keys and action related info as values | |
552 diverge: mapping of source name -> list of dest name for | |
553 divergent renames | |
554 renamedelete: mapping of source name -> list of destinations for files | |
555 deleted on one side and renamed on other. | |
556 """ | |
557 | |
558 self._actions = actions | |
559 self._diverge = diverge | |
560 self._renamedelete = renamedelete | |
561 | |
562 @property | |
563 def actions(self): | |
564 return self._actions | |
565 | |
566 @property | |
567 def diverge(self): | |
568 return self._diverge | |
569 | |
570 @property | |
571 def renamedelete(self): | |
572 return self._renamedelete | |
573 | |
574 def setactions(self, actions): | |
575 self._actions = actions | |
576 | |
577 | |
543 def manifestmerge( | 578 def manifestmerge( |
544 repo, | 579 repo, |
545 wctx, | 580 wctx, |
546 p2, | 581 p2, |
547 pa, | 582 pa, |
557 | 592 |
558 branchmerge and force are as passed in to update | 593 branchmerge and force are as passed in to update |
559 matcher = matcher to filter file lists | 594 matcher = matcher to filter file lists |
560 acceptremote = accept the incoming changes without prompting | 595 acceptremote = accept the incoming changes without prompting |
561 | 596 |
562 Returns: | 597 Returns an object of mergeresult class |
563 | |
564 actions: dict of filename as keys and action related info as values | |
565 diverge: mapping of source name -> list of dest name for divergent renames | |
566 renamedelete: mapping of source name -> list of destinations for files | |
567 deleted on one side and renamed on other. | |
568 """ | 598 """ |
569 if matcher is not None and matcher.always(): | 599 if matcher is not None and matcher.always(): |
570 matcher = None | 600 matcher = None |
571 | 601 |
572 # manifests fetched in order are going to be faster, so prime the caches | 602 # manifests fetched in order are going to be faster, so prime the caches |
843 _filternarrowactions(narrowmatch, branchmerge, actions) | 873 _filternarrowactions(narrowmatch, branchmerge, actions) |
844 | 874 |
845 renamedelete = branch_copies1.renamedelete | 875 renamedelete = branch_copies1.renamedelete |
846 renamedelete.update(branch_copies2.renamedelete) | 876 renamedelete.update(branch_copies2.renamedelete) |
847 | 877 |
848 return actions, diverge, renamedelete | 878 return mergeresult(actions, diverge, renamedelete) |
849 | 879 |
850 | 880 |
851 def _resolvetrivial(repo, wctx, mctx, ancestor, actions): | 881 def _resolvetrivial(repo, wctx, mctx, ancestor, actions): |
852 """Resolves false conflicts where the nodeid changed but the content | 882 """Resolves false conflicts where the nodeid changed but the content |
853 remained the same.""" | 883 remained the same.""" |
889 perform for merging two manifests. If there are multiple ancestors, uses bid | 919 perform for merging two manifests. If there are multiple ancestors, uses bid |
890 merge if enabled. | 920 merge if enabled. |
891 | 921 |
892 Also filters out actions which are unrequired if repository is sparse. | 922 Also filters out actions which are unrequired if repository is sparse. |
893 | 923 |
894 Returns same 3 element tuple as manifestmerge(). | 924 Returns mergeresult object same as manifestmerge(). |
895 """ | 925 """ |
896 # Avoid cycle. | 926 # Avoid cycle. |
897 from . import sparse | 927 from . import sparse |
898 | 928 |
899 if len(ancestors) == 1: # default | 929 if len(ancestors) == 1: # default |
900 actions, diverge, renamedelete = manifestmerge( | 930 mresult = manifestmerge( |
901 repo, | 931 repo, |
902 wctx, | 932 wctx, |
903 mctx, | 933 mctx, |
904 ancestors[0], | 934 ancestors[0], |
905 branchmerge, | 935 branchmerge, |
906 force, | 936 force, |
907 matcher, | 937 matcher, |
908 acceptremote, | 938 acceptremote, |
909 followcopies, | 939 followcopies, |
910 ) | 940 ) |
911 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce) | 941 _checkunknownfiles(repo, wctx, mctx, force, mresult.actions, mergeforce) |
912 | 942 |
913 else: # only when merge.preferancestor=* - the default | 943 else: # only when merge.preferancestor=* - the default |
914 repo.ui.note( | 944 repo.ui.note( |
915 _(b"note: merging %s and %s using bids from ancestors %s\n") | 945 _(b"note: merging %s and %s using bids from ancestors %s\n") |
916 % ( | 946 % ( |
925 {} | 955 {} |
926 ) # mapping filename to bids (action method to list af actions) | 956 ) # mapping filename to bids (action method to list af actions) |
927 diverge, renamedelete = None, None | 957 diverge, renamedelete = None, None |
928 for ancestor in ancestors: | 958 for ancestor in ancestors: |
929 repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor) | 959 repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor) |
930 actions, diverge1, renamedelete1 = manifestmerge( | 960 mresult1 = manifestmerge( |
931 repo, | 961 repo, |
932 wctx, | 962 wctx, |
933 mctx, | 963 mctx, |
934 ancestor, | 964 ancestor, |
935 branchmerge, | 965 branchmerge, |
937 matcher, | 967 matcher, |
938 acceptremote, | 968 acceptremote, |
939 followcopies, | 969 followcopies, |
940 forcefulldiff=True, | 970 forcefulldiff=True, |
941 ) | 971 ) |
942 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce) | 972 _checkunknownfiles( |
973 repo, wctx, mctx, force, mresult1.actions, mergeforce | |
974 ) | |
943 | 975 |
944 # Track the shortest set of warning on the theory that bid | 976 # Track the shortest set of warning on the theory that bid |
945 # merge will correctly incorporate more information | 977 # merge will correctly incorporate more information |
946 if diverge is None or len(diverge1) < len(diverge): | 978 if diverge is None or len(mresult1.diverge) < len(diverge): |
947 diverge = diverge1 | 979 diverge = mresult1.diverge |
948 if renamedelete is None or len(renamedelete) < len(renamedelete1): | 980 if renamedelete is None or len(renamedelete) < len( |
949 renamedelete = renamedelete1 | 981 mresult1.renamedelete |
950 | 982 ): |
951 for f, a in sorted(pycompat.iteritems(actions)): | 983 renamedelete = mresult1.renamedelete |
984 | |
985 for f, a in sorted(pycompat.iteritems(mresult1.actions)): | |
952 m, args, msg = a | 986 m, args, msg = a |
953 if m == mergestatemod.ACTION_GET_OTHER_AND_STORE: | 987 if m == mergestatemod.ACTION_GET_OTHER_AND_STORE: |
954 m = mergestatemod.ACTION_GET | 988 m = mergestatemod.ACTION_GET |
955 repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m)) | 989 repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m)) |
956 if f in fbids: | 990 if f in fbids: |
998 _(b' %s: ambiguous merge - picked %s action\n') % (f, m) | 1032 _(b' %s: ambiguous merge - picked %s action\n') % (f, m) |
999 ) | 1033 ) |
1000 actions[f] = l[0] | 1034 actions[f] = l[0] |
1001 continue | 1035 continue |
1002 repo.ui.note(_(b'end of auction\n\n')) | 1036 repo.ui.note(_(b'end of auction\n\n')) |
1037 mresult = mergeresult(actions, diverge, renamedelete) | |
1003 | 1038 |
1004 if wctx.rev() is None: | 1039 if wctx.rev() is None: |
1005 fractions = _forgetremoved(wctx, mctx, branchmerge) | 1040 fractions = _forgetremoved(wctx, mctx, branchmerge) |
1006 actions.update(fractions) | 1041 mresult.actions.update(fractions) |
1007 | 1042 |
1008 prunedactions = sparse.filterupdatesactions( | 1043 prunedactions = sparse.filterupdatesactions( |
1009 repo, wctx, mctx, branchmerge, actions | 1044 repo, wctx, mctx, branchmerge, mresult.actions |
1010 ) | 1045 ) |
1011 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions) | 1046 _resolvetrivial(repo, wctx, mctx, ancestors[0], mresult.actions) |
1012 | 1047 |
1013 return prunedactions, diverge, renamedelete | 1048 mresult.setactions(prunedactions) |
1049 return mresult | |
1014 | 1050 |
1015 | 1051 |
1016 def _getcwd(): | 1052 def _getcwd(): |
1017 try: | 1053 try: |
1018 return encoding.getcwd() | 1054 return encoding.getcwd() |
1732 followcopies = False | 1768 followcopies = False |
1733 if not branchmerge and not wc.dirty(missing=True): | 1769 if not branchmerge and not wc.dirty(missing=True): |
1734 followcopies = False | 1770 followcopies = False |
1735 | 1771 |
1736 ### calculate phase | 1772 ### calculate phase |
1737 actionbyfile, diverge, renamedelete = calculateupdates( | 1773 mresult = calculateupdates( |
1738 repo, | 1774 repo, |
1739 wc, | 1775 wc, |
1740 p2, | 1776 p2, |
1741 pas, | 1777 pas, |
1742 branchmerge, | 1778 branchmerge, |
1744 mergeancestor, | 1780 mergeancestor, |
1745 followcopies, | 1781 followcopies, |
1746 matcher=matcher, | 1782 matcher=matcher, |
1747 mergeforce=mergeforce, | 1783 mergeforce=mergeforce, |
1748 ) | 1784 ) |
1785 | |
1786 actionbyfile = mresult.actions | |
1749 | 1787 |
1750 if updatecheck == UPDATECHECK_NO_CONFLICT: | 1788 if updatecheck == UPDATECHECK_NO_CONFLICT: |
1751 for f, (m, args, msg) in pycompat.iteritems(actionbyfile): | 1789 for f, (m, args, msg) in pycompat.iteritems(actionbyfile): |
1752 if m not in ( | 1790 if m not in ( |
1753 mergestatemod.ACTION_GET, | 1791 mergestatemod.ACTION_GET, |
1838 _checkcollision(repo, p2.manifest(), None) | 1876 _checkcollision(repo, p2.manifest(), None) |
1839 else: | 1877 else: |
1840 _checkcollision(repo, wc.manifest(), actions) | 1878 _checkcollision(repo, wc.manifest(), actions) |
1841 | 1879 |
1842 # divergent renames | 1880 # divergent renames |
1843 for f, fl in sorted(pycompat.iteritems(diverge)): | 1881 for f, fl in sorted(pycompat.iteritems(mresult.diverge)): |
1844 repo.ui.warn( | 1882 repo.ui.warn( |
1845 _( | 1883 _( |
1846 b"note: possible conflict - %s was renamed " | 1884 b"note: possible conflict - %s was renamed " |
1847 b"multiple times to:\n" | 1885 b"multiple times to:\n" |
1848 ) | 1886 ) |
1850 ) | 1888 ) |
1851 for nf in sorted(fl): | 1889 for nf in sorted(fl): |
1852 repo.ui.warn(b" %s\n" % nf) | 1890 repo.ui.warn(b" %s\n" % nf) |
1853 | 1891 |
1854 # rename and delete | 1892 # rename and delete |
1855 for f, fl in sorted(pycompat.iteritems(renamedelete)): | 1893 for f, fl in sorted(pycompat.iteritems(mresult.renamedelete)): |
1856 repo.ui.warn( | 1894 repo.ui.warn( |
1857 _( | 1895 _( |
1858 b"note: possible conflict - %s was deleted " | 1896 b"note: possible conflict - %s was deleted " |
1859 b"and renamed to:\n" | 1897 b"and renamed to:\n" |
1860 ) | 1898 ) |