comparison mercurial/phases.py @ 51580:b70628a9aa7e

phases: use revision number in new_heads All graph operations will be done using revision numbers, so passing nodes only means they will eventually get converted to revision numbers internally. As part of an effort to align the code on using revision number we make the `phases.newheads` function operated on revision number, taking them as input and using them in returns, instead of the node-id it used to consume and produce. This is part of multiple changesets effort to translate more part of the logic, but is done step by step to facilitate the identification of issue that might arise in mercurial core and extensions. To make the change simpler to handle for third party extensions, we also rename the function, using a more modern form. This will help detecting the different between the node-id version and the rev-num version. I also take this as an opportunity to add some comment about possible performance improvement for the future. They don't matter too much now, but they are worse exploring in a while.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 05 Apr 2024 11:33:47 +0200
parents 87655e6dc108
children e0194b3ea312
comparison
equal deleted inserted replaced
51579:87655e6dc108 51580:b70628a9aa7e
107 import weakref 107 import weakref
108 108
109 from typing import ( 109 from typing import (
110 Any, 110 Any,
111 Callable, 111 Callable,
112 Collection,
112 Dict, 113 Dict,
113 Iterable, 114 Iterable,
114 List, 115 List,
115 Optional, 116 Optional,
116 Set, 117 Set,
125 short, 126 short,
126 wdirrev, 127 wdirrev,
127 ) 128 )
128 from . import ( 129 from . import (
129 error, 130 error,
130 pycompat,
131 requirements, 131 requirements,
132 smartset, 132 smartset,
133 txnutil, 133 txnutil,
134 util, 134 util,
135 ) 135 )
1123 draft_roots.append(rev) 1123 draft_roots.append(rev)
1124 else: 1124 else:
1125 msg = _(b'ignoring unexpected root from remote: %i %s\n') 1125 msg = _(b'ignoring unexpected root from remote: %i %s\n')
1126 repo.ui.warn(msg % (phase, nhex)) 1126 repo.ui.warn(msg % (phase, nhex))
1127 # compute heads 1127 # compute heads
1128 subset_revs = [to_rev(n) for n in subset]
1129 public_heads = new_heads(repo, subset_revs, draft_roots)
1128 draft_nodes = [to_node(r) for r in draft_roots] 1130 draft_nodes = [to_node(r) for r in draft_roots]
1129 publicheads = newheads(repo, subset, draft_nodes) 1131 public_nodes = [to_node(r) for r in public_heads]
1130 return publicheads, draft_nodes 1132 return public_nodes, draft_nodes
1131 1133
1132 1134
1133 class remotephasessummary: 1135 class remotephasessummary:
1134 """summarize phase information on the remote side 1136 """summarize phase information on the remote side
1135 1137
1150 # Get the list of all "heads" revs draft on remote 1152 # Get the list of all "heads" revs draft on remote
1151 dheads = unfi.set(b'heads(%ln::%ln)', self.draftroots, remotesubset) 1153 dheads = unfi.set(b'heads(%ln::%ln)', self.draftroots, remotesubset)
1152 self.draftheads = [c.node() for c in dheads] 1154 self.draftheads = [c.node() for c in dheads]
1153 1155
1154 1156
1155 def newheads(repo, heads, roots): 1157 def new_heads(
1158 repo,
1159 heads: Collection[int],
1160 roots: Collection[int],
1161 ) -> Collection[int]:
1156 """compute new head of a subset minus another 1162 """compute new head of a subset minus another
1157 1163
1158 * `heads`: define the first subset 1164 * `heads`: define the first subset
1159 * `roots`: define the second we subtract from the first""" 1165 * `roots`: define the second we subtract from the first"""
1160 # prevent an import cycle 1166 # prevent an import cycle
1161 # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases 1167 # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases
1162 from . import dagop 1168 from . import dagop
1163 1169
1164 repo = repo.unfiltered()
1165 cl = repo.changelog
1166 rev = cl.index.get_rev
1167 if not roots: 1170 if not roots:
1168 return heads 1171 return heads
1169 if not heads or heads == [repo.nullid]: 1172 if not heads or heads == [nullrev]:
1170 return [] 1173 return []
1171 # The logic operated on revisions, convert arguments early for convenience 1174 # The logic operated on revisions, convert arguments early for convenience
1172 new_heads = {rev(n) for n in heads if n != repo.nullid} 1175 # PERF-XXX: maybe heads could directly comes as a set without impacting
1173 roots = [rev(n) for n in roots] 1176 # other user of that value
1177 new_heads = set(heads)
1178 new_heads.discard(nullrev)
1174 # compute the area we need to remove 1179 # compute the area we need to remove
1175 affected_zone = repo.revs(b"(%ld::%ld)", roots, new_heads) 1180 affected_zone = repo.revs(b"(%ld::%ld)", roots, new_heads)
1176 # heads in the area are no longer heads 1181 # heads in the area are no longer heads
1177 new_heads.difference_update(affected_zone) 1182 new_heads.difference_update(affected_zone)
1178 # revisions in the area have children outside of it, 1183 # revisions in the area have children outside of it,
1186 new_heads.update(candidates) 1191 new_heads.update(candidates)
1187 prunestart = repo.revs(b"parents(%ld) and not null", new_heads) 1192 prunestart = repo.revs(b"parents(%ld) and not null", new_heads)
1188 pruned = dagop.reachableroots(repo, candidates, prunestart) 1193 pruned = dagop.reachableroots(repo, candidates, prunestart)
1189 new_heads.difference_update(pruned) 1194 new_heads.difference_update(pruned)
1190 1195
1191 return pycompat.maplist(cl.node, sorted(new_heads)) 1196 # PERF-XXX: do we actually need a sorted list here? Could we simply return
1197 # a set?
1198 return sorted(new_heads)
1192 1199
1193 1200
1194 def newcommitphase(ui: "uimod.ui") -> int: 1201 def newcommitphase(ui: "uimod.ui") -> int:
1195 """helper to get the target phase of new commit 1202 """helper to get the target phase of new commit
1196 1203