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