Mercurial > public > mercurial-scm > hg
comparison mercurial/scmutil.py @ 39891:b99903534e06
scmutil: accept multiple predecessors in 'replacements' (API)
This changeset makes 'cleanupnodes' accepts multiple predecessors as
`replacements` keys. The same as it accepts multiple successors as
`replacements` values. To avoid breaking all callers, the old and new ways are
currently valid at the same time. We'll deprecate and drop the old way later.
This change is the first step toward a better tracking of "fold" event in the
evolution history. While working on the "rewind" command (in the evolve
extension), we realized that first class tracking of folds are necessary.
We already have good tracking of splits. When walking the evolution history
from predecessors to successors, that makes for a clear distinction between
having multiple successors because of the actual splitting of a changeset or
content-divergences.
The "rewind" command allows restoring older evolution of a stack of
changesets. One of its mode walks the evolution history to automatically find
appropriate predecessors. This means walking from successors to predecessors.
In this case, we need to be able to make the same distinction between an
actual fold and other cases. So we will have to track folds explicitly.
This changesets only focus on making it possible to express fold at the
`cleanupnodes` API level. The actual tracking will be implemented later.
author | Boris Feld <boris.feld@octobus.net> |
---|---|
date | Thu, 27 Sep 2018 13:57:50 -0700 |
parents | 1c3f1491965f |
children | d739f423bf06 |
comparison
equal
deleted
inserted
replaced
39890:1c3f1491965f | 39891:b99903534e06 |
---|---|
873 if not replacements and not moves: | 873 if not replacements and not moves: |
874 return | 874 return |
875 | 875 |
876 # translate mapping's other forms | 876 # translate mapping's other forms |
877 if not util.safehasattr(replacements, 'items'): | 877 if not util.safehasattr(replacements, 'items'): |
878 replacements = {n: () for n in replacements} | 878 replacements = {(n,): () for n in replacements} |
879 else: | |
880 # upgrading non tuple "source" to tuple ones for BC | |
881 repls = {} | |
882 for key, value in replacements.items(): | |
883 if not isinstance(key, tuple): | |
884 key = (key,) | |
885 repls[key] = value | |
886 replacements = repls | |
879 | 887 |
880 # Calculate bookmark movements | 888 # Calculate bookmark movements |
881 if moves is None: | 889 if moves is None: |
882 moves = {} | 890 moves = {} |
883 # Unfiltered repo is needed since nodes in replacements might be hidden. | 891 # Unfiltered repo is needed since nodes in replacements might be hidden. |
884 unfi = repo.unfiltered() | 892 unfi = repo.unfiltered() |
885 for oldnode, newnodes in replacements.items(): | 893 for oldnodes, newnodes in replacements.items(): |
886 if oldnode in moves: | 894 for oldnode in oldnodes: |
887 continue | 895 if oldnode in moves: |
888 if len(newnodes) > 1: | 896 continue |
889 # usually a split, take the one with biggest rev number | 897 if len(newnodes) > 1: |
890 newnode = next(unfi.set('max(%ln)', newnodes)).node() | 898 # usually a split, take the one with biggest rev number |
891 elif len(newnodes) == 0: | 899 newnode = next(unfi.set('max(%ln)', newnodes)).node() |
892 # move bookmark backwards | 900 elif len(newnodes) == 0: |
893 roots = list(unfi.set('max((::%n) - %ln)', oldnode, | 901 # move bookmark backwards |
894 list(replacements))) | 902 allreplaced = [] |
895 if roots: | 903 for rep in replacements: |
896 newnode = roots[0].node() | 904 allreplaced.extend(rep) |
905 roots = list(unfi.set('max((::%n) - %ln)', oldnode, | |
906 allreplaced)) | |
907 if roots: | |
908 newnode = roots[0].node() | |
909 else: | |
910 newnode = nullid | |
897 else: | 911 else: |
898 newnode = nullid | 912 newnode = newnodes[0] |
899 else: | 913 moves[oldnode] = newnode |
900 newnode = newnodes[0] | |
901 moves[oldnode] = newnode | |
902 | 914 |
903 allnewnodes = [n for ns in replacements.values() for n in ns] | 915 allnewnodes = [n for ns in replacements.values() for n in ns] |
904 toretract = {} | 916 toretract = {} |
905 toadvance = {} | 917 toadvance = {} |
906 if fixphase: | 918 if fixphase: |
907 precursors = {} | 919 precursors = {} |
908 for oldnode, newnodes in replacements.items(): | 920 for oldnodes, newnodes in replacements.items(): |
909 for newnode in newnodes: | 921 for oldnode in oldnodes: |
910 precursors.setdefault(newnode, []).append(oldnode) | 922 for newnode in newnodes: |
923 precursors.setdefault(newnode, []).append(oldnode) | |
911 | 924 |
912 allnewnodes.sort(key=lambda n: unfi[n].rev()) | 925 allnewnodes.sort(key=lambda n: unfi[n].rev()) |
913 newphases = {} | 926 newphases = {} |
914 def phase(ctx): | 927 def phase(ctx): |
915 return newphases.get(ctx.node(), ctx.phase()) | 928 return newphases.get(ctx.node(), ctx.phase()) |
965 # Also sort the node in topology order, that might be useful for | 978 # Also sort the node in topology order, that might be useful for |
966 # some obsstore logic. | 979 # some obsstore logic. |
967 # NOTE: the filtering and sorting might belong to createmarkers. | 980 # NOTE: the filtering and sorting might belong to createmarkers. |
968 isobs = unfi.obsstore.successors.__contains__ | 981 isobs = unfi.obsstore.successors.__contains__ |
969 torev = unfi.changelog.rev | 982 torev = unfi.changelog.rev |
970 sortfunc = lambda ns: torev(ns[0]) | 983 sortfunc = lambda ns: torev(ns[0][0]) |
971 rels = [] | 984 rels = [] |
972 for n, s in sorted(replacements.items(), key=sortfunc): | 985 for ns, s in sorted(replacements.items(), key=sortfunc): |
973 if s or not isobs(n): | 986 for n in ns: |
974 rel = (unfi[n], tuple(unfi[m] for m in s)) | 987 if s or not isobs(n): |
975 rels.append(rel) | 988 rel = (unfi[n], tuple(unfi[m] for m in s)) |
989 rels.append(rel) | |
976 if rels: | 990 if rels: |
977 obsolete.createmarkers(repo, rels, operation=operation, | 991 obsolete.createmarkers(repo, rels, operation=operation, |
978 metadata=metadata) | 992 metadata=metadata) |
979 else: | 993 else: |
980 from . import repair # avoid import cycle | 994 from . import repair # avoid import cycle |
981 tostrip = list(replacements) | 995 tostrip = list(n for ns in replacements for n in ns) |
982 if tostrip: | 996 if tostrip: |
983 repair.delayedstrip(repo.ui, repo, tostrip, operation, | 997 repair.delayedstrip(repo.ui, repo, tostrip, operation, |
984 backup=backup) | 998 backup=backup) |
985 | 999 |
986 def addremove(repo, matcher, prefix, opts=None): | 1000 def addremove(repo, matcher, prefix, opts=None): |