comparison mercurial/graphmod.py @ 28376:fa2cd0c9a567

graphmod: augment the graph to include more information about the edges The walker knows when an edge leads to a direct parent, a grandparent (skipping revisions not part of the revset) and parents that are missing altogether (neither it nor a grandparent is in the revset). Add this information to the parents sequence yielded.
author Martijn Pieters <mjpieters@fb.com>
date Fri, 04 Mar 2016 14:44:32 +0000
parents 97cb1aeaca78
children 0d6137891114
comparison
equal deleted inserted replaced
28375:97cb1aeaca78 28376:fa2cd0c9a567
26 revset, 26 revset,
27 util, 27 util,
28 ) 28 )
29 29
30 CHANGESET = 'C' 30 CHANGESET = 'C'
31 PARENT = 'P'
32 GRANDPARENT = 'G'
33 MISSINGPARENT = 'M'
31 34
32 def groupbranchiter(revs, parentsfunc, firstbranch=()): 35 def groupbranchiter(revs, parentsfunc, firstbranch=()):
33 """Yield revisions from heads to roots one (topo) branch at a time. 36 """Yield revisions from heads to roots one (topo) branch at a time.
34 37
35 This function aims to be used by a graph generator that wishes to minimize 38 This function aims to be used by a graph generator that wishes to minimize
226 for g in groups: 229 for g in groups:
227 for r in g[0]: 230 for r in g[0]:
228 yield r 231 yield r
229 232
230 def dagwalker(repo, revs): 233 def dagwalker(repo, revs):
231 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples 234 """cset DAG generator yielding (id, CHANGESET, ctx, [parentinfo]) tuples
232 235
233 This generator function walks through revisions (which should be ordered 236 This generator function walks through revisions (which should be ordered
234 from bigger to lower). It returns a tuple for each node. The node and parent 237 from bigger to lower). It returns a tuple for each node.
235 ids are arbitrary integers which identify a node in the context of the graph 238
239 Each parentinfo entry is a tuple with (edgetype, parentid), where edgetype
240 is one of PARENT, GRANDPARENT or MISSINGPARENT. The node and parent ids
241 are arbitrary integers which identify a node in the context of the graph
236 returned. 242 returned.
243
237 """ 244 """
238 if not revs: 245 if not revs:
239 return 246 return
240 247
241 gpcache = {} 248 gpcache = {}
250 revs = groupbranchiter(revs, parentrevs, firstbranch) 257 revs = groupbranchiter(revs, parentrevs, firstbranch)
251 revs = revset.baseset(revs) 258 revs = revset.baseset(revs)
252 259
253 for rev in revs: 260 for rev in revs:
254 ctx = repo[rev] 261 ctx = repo[rev]
255 parents = sorted(set([p.rev() for p in ctx.parents() 262 # partition into parents in the rev set and missing parents, then
256 if p.rev() in revs])) 263 # augment the lists with markers, to inform graph drawing code about
257 mpars = [p.rev() for p in ctx.parents() if 264 # what kind of edge to draw between nodes.
258 p.rev() != nullrev and p.rev() not in parents] 265 pset = set(p.rev() for p in ctx.parents() if p.rev() in revs)
266 mpars = [p.rev() for p in ctx.parents()
267 if p.rev() != nullrev and p.rev() not in pset]
268 parents = [(PARENT, p) for p in sorted(pset)]
259 269
260 for mpar in mpars: 270 for mpar in mpars:
261 gp = gpcache.get(mpar) 271 gp = gpcache.get(mpar)
262 if gp is None: 272 if gp is None:
263 # precompute slow query as we know reachableroots() goes 273 # precompute slow query as we know reachableroots() goes
264 # through all revs (issue4782) 274 # through all revs (issue4782)
265 if not isinstance(revs, revset.baseset): 275 if not isinstance(revs, revset.baseset):
266 revs = revset.baseset(revs) 276 revs = revset.baseset(revs)
267 gp = gpcache[mpar] = revset.reachableroots(repo, revs, [mpar]) 277 gp = gpcache[mpar] = sorted(set(revset.reachableroots(
278 repo, revs, [mpar])))
268 if not gp: 279 if not gp:
269 parents.append(mpar) 280 parents.append((MISSINGPARENT, mpar))
281 pset.add(mpar)
270 else: 282 else:
271 parents.extend(g for g in gp if g not in parents) 283 parents.extend((GRANDPARENT, g) for g in gp if g not in pset)
284 pset.update(gp)
272 285
273 yield (ctx.rev(), CHANGESET, ctx, parents) 286 yield (ctx.rev(), CHANGESET, ctx, parents)
274 287
275 def nodes(repo, nodes): 288 def nodes(repo, nodes):
276 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples 289 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
279 that are in nodes, too. 292 that are in nodes, too.
280 """ 293 """
281 include = set(nodes) 294 include = set(nodes)
282 for node in nodes: 295 for node in nodes:
283 ctx = repo[node] 296 ctx = repo[node]
284 parents = set([p.rev() for p in ctx.parents() if p.node() in include]) 297 parents = set((PARENT, p.rev()) for p in ctx.parents()
298 if p.node() in include)
285 yield (ctx.rev(), CHANGESET, ctx, sorted(parents)) 299 yield (ctx.rev(), CHANGESET, ctx, sorted(parents))
286 300
287 def colored(dag, repo): 301 def colored(dag, repo):
288 """annotates a DAG with colored edge information 302 """annotates a DAG with colored edge information
289 303
328 col = seen.index(cur) 342 col = seen.index(cur)
329 color = colors.pop(cur) 343 color = colors.pop(cur)
330 next = seen[:] 344 next = seen[:]
331 345
332 # Add parents to next 346 # Add parents to next
333 addparents = [p for p in parents if p not in next] 347 addparents = [p for pt, p in parents if p not in next]
334 next[col:col + 1] = addparents 348 next[col:col + 1] = addparents
335 349
336 # Set colors for the parents 350 # Set colors for the parents
337 for i, p in enumerate(addparents): 351 for i, p in enumerate(addparents):
338 if not i: 352 if not i:
349 edges.append(( 363 edges.append((
350 ecol, next.index(eid), colors[eid], 364 ecol, next.index(eid), colors[eid],
351 bconf.get('width', -1), 365 bconf.get('width', -1),
352 bconf.get('color', ''))) 366 bconf.get('color', '')))
353 elif eid == cur: 367 elif eid == cur:
354 for p in parents: 368 for ptype, p in parents:
355 bconf = getconf(p) 369 bconf = getconf(p)
356 edges.append(( 370 edges.append((
357 ecol, next.index(p), color, 371 ecol, next.index(p), color,
358 bconf.get('width', -1), 372 bconf.get('width', -1),
359 bconf.get('color', ''))) 373 bconf.get('color', '')))
369 seen.append(rev) 383 seen.append(rev)
370 nodeidx = seen.index(rev) 384 nodeidx = seen.index(rev)
371 385
372 knownparents = [] 386 knownparents = []
373 newparents = [] 387 newparents = []
374 for parent in parents: 388 for ptype, parent in parents:
375 if parent in seen: 389 if parent in seen:
376 knownparents.append(parent) 390 knownparents.append(parent)
377 else: 391 else:
378 newparents.append(parent) 392 newparents.append(parent)
379 393