##// END OF EJS Templates
changegroup: don't try to build changelog chunks if not required...
changegroup: don't try to build changelog chunks if not required When we extend a narrow clone without ellipsis, we don't download changelog information because that's already present with the client. However we still try to build that chunk stream. Building that chunk stream involves calling a lookup function and store.emitrevisions() API. The lookup function is called len(cl) number of times. On large repositories, where len(cl) is in millions, calling that lookup function is not a good idea. Also it's not required to use the store.emitrevisons() API because we already have nodes present which we can use. This patch short-circuits state building logic if we are processing a non-ellipsis case and changelog is not required. This saves up ~20 seconds on our internal repo for a single extend call. Differential Revision: https://phab.mercurial-scm.org/D5733

File last commit:

r39322:3c4b2e88 default
r41491:fa7d61f9 default
Show More
graphmod.py
495 lines | 17.0 KiB | text/x-python | PythonLexer
Dirkjan Ochtman
add graph page to hgweb
r6691 # Revision graph generator for Mercurial
#
# Copyright 2008 Dirkjan Ochtman <dirkjan@ochtman.nl>
# Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 """supports walking the history as DAGs suitable for graphical output
The most basic format we use is that of::
(id, type, data, [parentids])
The node and parent ids are arbitrary integers which identify a node in the
context of the graph returned. Type is a constant specifying the node type.
Data depends on type.
"""
Gregory Szorc
graphmod: use absolute_import
r25951 from __future__ import absolute_import
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840
Gregory Szorc
graphmod: use absolute_import
r25951 from .node import nullrev
Laurent Charignon
revset: remove grandparent by using reachableroots...
r26003 from . import (
Yuya Nishihara
dagop: split module hosting DAG-related algorithms from revset...
r32903 dagop,
Gregory Szorc
global: use pycompat.xrange()...
r38806 pycompat,
Yuya Nishihara
revset: import set classes directly from smartset module...
r31023 smartset,
Laurent Charignon
revset: remove grandparent by using reachableroots...
r26003 util,
)
Gregory Szorc
graphmod: use absolute_import
r25951
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 CHANGESET = 'C'
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 PARENT = 'P'
GRANDPARENT = 'G'
MISSINGPARENT = 'M'
Martijn Pieters
graphmod: allow edges to end early...
r28601 # Style of line to draw. None signals a line that ends and is removed at this
Martijn Pieters
graphmod: partial edge styling...
r29134 # point. A number prefix means only the last N characters of the current block
# will use that style, the rest will use the PARENT style. Add a - sign
# (so making N negative) and all but the first N characters use that style.
Martijn Pieters
graphmod: set default edge styles for ascii graphs (BC)...
r28627 EDGES = {PARENT: '|', GRANDPARENT: ':', MISSINGPARENT: None}
Dirkjan Ochtman
add graph page to hgweb
r6691
Alexander Solovyov
graphmod: use revsets internally...
r14042 def dagwalker(repo, revs):
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 """cset DAG generator yielding (id, CHANGESET, ctx, [parentinfo]) tuples
Alexander Solovyov
graphmod: use revsets internally...
r14042
This generator function walks through revisions (which should be ordered
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 from bigger to lower). It returns a tuple for each node.
Each parentinfo entry is a tuple with (edgetype, parentid), where edgetype
is one of PARENT, GRANDPARENT or MISSINGPARENT. The node and parent ids
are arbitrary integers which identify a node in the context of the graph
Alexander Solovyov
graphmod: use revsets internally...
r14042 returned.
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376
Peter Arrenbrecht
graphmod/graphlog: move log walks to graphmod
r8836 """
Alexander Solovyov
graphmod: use revsets internally...
r14042 gpcache = {}
Idan Kamara
graphmod: restore generator nature of dagwalker...
r14087 for rev in revs:
ctx = repo[rev]
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 # partition into parents in the rev set and missing parents, then
# augment the lists with markers, to inform graph drawing code about
# what kind of edge to draw between nodes.
pset = set(p.rev() for p in ctx.parents() if p.rev() in revs)
mpars = [p.rev() for p in ctx.parents()
if p.rev() != nullrev and p.rev() not in pset]
parents = [(PARENT, p) for p in sorted(pset)]
Alexander Solovyov
graphmod: use revsets internally...
r14042
for mpar in mpars:
Patrick Mezard
graphmod: correctly emit nodes with more than 2 predecessors...
r14131 gp = gpcache.get(mpar)
Alexander Solovyov
graphmod: use revsets internally...
r14042 if gp is None:
Yuya Nishihara
graphmod: compute slow revset query once prior to reachableroots (issue4782)...
r26187 # precompute slow query as we know reachableroots() goes
# through all revs (issue4782)
Yuya Nishihara
revset: import set classes directly from smartset module...
r31023 if not isinstance(revs, smartset.baseset):
revs = smartset.baseset(revs)
Yuya Nishihara
dagop: split module hosting DAG-related algorithms from revset...
r32903 gp = gpcache[mpar] = sorted(set(dagop.reachableroots(
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 repo, revs, [mpar])))
Patrick Mezard
graphmod: correctly emit nodes with more than 2 predecessors...
r14131 if not gp:
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 parents.append((MISSINGPARENT, mpar))
pset.add(mpar)
Patrick Mezard
graphmod: correctly emit nodes with more than 2 predecessors...
r14131 else:
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 parents.extend((GRANDPARENT, g) for g in gp if g not in pset)
pset.update(gp)
Alexander Solovyov
graphmod: use revsets internally...
r14042
Idan Kamara
graphmod: restore generator nature of dagwalker...
r14087 yield (ctx.rev(), CHANGESET, ctx, parents)
Peter Arrenbrecht
graphmod/graphlog: move log walks to graphmod
r8836
Peter Arrenbrecht
graphmod/graphlog: extract nodelistwalk
r8837 def nodes(repo, nodes):
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
This generator function walks the given nodes. It only returns parents
that are in nodes, too.
"""
Peter Arrenbrecht
graphmod/graphlog: extract nodelistwalk
r8837 include = set(nodes)
for node in nodes:
ctx = repo[node]
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 parents = set((PARENT, p.rev()) for p in ctx.parents()
if p.node() in include)
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 yield (ctx.rev(), CHANGESET, ctx, sorted(parents))
Peter Arrenbrecht
graphmod/graphlog: extract nodelistwalk
r8837
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 def colored(dag, repo):
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 """annotates a DAG with colored edge information
For each DAG node this function emits tuples::
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 (id, type, data, (col, color), [(col, nextcol, color)])
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 with the following new elements:
Peter Arrenbrecht
graphmod: code cleanup and doc fix
r8835 - Tuple (col, color) with column and color index for the current node
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 - A list of tuples indicating the edges between the current node and its
parents.
Dirkjan Ochtman
add graph page to hgweb
r6691 """
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 seen = []
Dirkjan Ochtman
add graph page to hgweb
r6691 colors = {}
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 newcolor = 1
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 config = {}
for key, val in repo.ui.configitems('graph'):
Matt Mackall
graphmod: rewrite graph config validation...
r16131 if '.' in key:
branch, setting = key.rsplit('.', 1)
# Validation
if setting == "width" and val.isdigit():
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 config.setdefault(branch, {})[setting] = int(val)
Matt Mackall
graphmod: rewrite graph config validation...
r16131 elif setting == "color" and val.isalnum():
config.setdefault(branch, {})[setting] = val
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129
Matt Mackall
graphmod: add config cache...
r16132 if config:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 getconf = util.lrucachefunc(
lambda rev: config.get(repo[rev].branch(), {}))
Matt Mackall
graphmod: add config cache...
r16132 else:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 getconf = lambda rev: {}
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 for (cur, type, data, parents) in dag:
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 # Compute seen and next
if cur not in seen:
seen.append(cur) # new head
colors[cur] = newcolor
newcolor += 1
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 col = seen.index(cur)
color = colors.pop(cur)
next = seen[:]
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 # Add parents to next
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 addparents = [p for pt, p in parents if p not in next]
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 next[col:col + 1] = addparents
Dirkjan Ochtman
add graph page to hgweb
r6691
# Set colors for the parents
for i, p in enumerate(addparents):
if not i:
colors[p] = color
else:
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 colors[p] = newcolor
newcolor += 1
Dirkjan Ochtman
add graph page to hgweb
r6691
# Add edges to the graph
edges = []
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 for ecol, eid in enumerate(seen):
if eid in next:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf = getconf(eid)
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 edges.append((
ecol, next.index(eid), colors[eid],
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf.get('width', -1),
bconf.get('color', '')))
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 elif eid == cur:
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 for ptype, p in parents:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf = getconf(p)
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 edges.append((
ecol, next.index(p), color,
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf.get('width', -1),
bconf.get('color', '')))
Dirkjan Ochtman
add graph page to hgweb
r6691
# Yield and move on
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 yield (cur, type, data, (col, color), edges)
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 seen = next
Alexander Solovyov
graphmod: use revsets internally...
r14042
Danny Hooper
log: add a "graphwidth" template variable...
r33860 def asciiedges(type, char, state, rev, parents):
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 """adds edge info to changelog DAG walk suitable for ascii()"""
Martijn Pieters
graphmod: refactor state handling...
r28375 seen = state['seen']
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if rev not in seen:
seen.append(rev)
nodeidx = seen.index(rev)
knownparents = []
newparents = []
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 for ptype, parent in parents:
Yuya Nishihara
graphlog: draw multiple edges towards null node (issue5440)...
r31552 if parent == rev:
# self reference (should only be seen in null rev)
continue
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if parent in seen:
knownparents.append(parent)
else:
newparents.append(parent)
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 state['edges'][parent] = state['styles'].get(ptype, '|')
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
ncols = len(seen)
Danny Hooper
log: add a "graphwidth" template variable...
r33860 width = 1 + ncols * 2
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 nextseen = seen[:]
nextseen[nodeidx:nodeidx + 1] = newparents
Yuya Nishihara
graphlog: draw multiple edges towards null node (issue5440)...
r31552 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
Martijn Pieters
graphmod: fix seen state handling for > 2 parents (issue5174)...
r28998 seen[:] = nextseen
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 while len(newparents) > 2:
# ascii() only knows how to add or remove a single column between two
# calls. Nodes with more than two parents break this constraint so we
# introduce intermediate expansion lines to grow the active node list
# slowly.
edges.append((nodeidx, nodeidx))
edges.append((nodeidx, nodeidx + 1))
nmorecols = 1
Danny Hooper
log: add a "graphwidth" template variable...
r33860 width += 2
yield (type, char, width, (nodeidx, edges, ncols, nmorecols))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 char = '\\'
nodeidx += 1
ncols += 1
edges = []
del newparents[0]
if len(newparents) > 0:
edges.append((nodeidx, nodeidx))
if len(newparents) > 1:
edges.append((nodeidx, nodeidx + 1))
nmorecols = len(nextseen) - ncols
Danny Hooper
log: add a "graphwidth" template variable...
r33860 if nmorecols > 0:
width += 2
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # remove current node from edge characters, no longer needed
state['edges'].pop(rev, None)
Danny Hooper
log: add a "graphwidth" template variable...
r33860 yield (type, char, width, (nodeidx, edges, ncols, nmorecols))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
def _fixlongrightedges(edges):
for (i, (start, end)) in enumerate(edges):
if end > start:
edges[i] = (start, end + 1)
def _getnodelineedgestail(
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 echars, idx, pidx, ncols, coldiff, pdiff, fix_tail):
if fix_tail and coldiff == pdiff and coldiff != 0:
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # Still going in the same non-vertical direction.
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 if coldiff == -1:
start = max(idx + 1, pidx)
tail = echars[idx * 2:(start - 1) * 2]
tail.extend(["/", " "] * (ncols - start))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 return tail
else:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 return ["\\", " "] * (ncols - idx - 1)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 remainder = (ncols - idx - 1)
return echars[-(remainder * 2):] if remainder > 0 else []
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 def _drawedges(echars, edges, nodeline, interline):
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 for (start, end) in edges:
if start == end + 1:
interline[2 * end + 1] = "/"
elif start == end - 1:
interline[2 * start + 1] = "\\"
elif start == end:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 interline[2 * start] = echars[2 * start]
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
if 2 * end >= len(nodeline):
continue
nodeline[2 * end] = "+"
if start > end:
(start, end) = (end, start)
for i in range(2 * start + 1, 2 * end):
if nodeline[i] != "+":
nodeline[i] = "-"
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 def _getpaddingline(echars, idx, ncols, edges):
# all edges up to the current node
line = echars[:idx * 2]
# an edge for the current node, if there is one
if (idx, idx - 1) in edges or (idx, idx) in edges:
# (idx, idx - 1) (idx, idx)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # | | | | | | | |
# +---o | | o---+
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # | | X | | X | |
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # | |/ / | |/ /
# | | | | | |
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 line.extend(echars[idx * 2:(idx + 1) * 2])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 line.extend([' ', ' '])
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # all edges to the right of the current node
remainder = ncols - idx - 1
if remainder > 0:
line.extend(echars[-(remainder * 2):])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 return line
Kyle Lippincott
log: respect graphshorten on terminal nodes (collapsing o-~ to just o~)...
r39322 def _drawendinglines(lines, extra, edgemap, seen, state):
Martijn Pieters
graphmod: allow edges to end early...
r28601 """Draw ending lines for missing parent edges
None indicates an edge that ends at between this node and the next
Replace with a short line ending in ~ and add / lines to any edges to
the right.
"""
if None not in edgemap.values():
return
# Check for more edges to the right of our ending edges.
# We need enough space to draw adjustment lines for these.
edgechars = extra[::2]
while edgechars and edgechars[-1] is None:
edgechars.pop()
shift_size = max((edgechars.count(None) * 2) - 1, 0)
Kyle Lippincott
log: respect graphshorten on terminal nodes (collapsing o-~ to just o~)...
r39322 minlines = 3 if not state['graphshorten'] else 2
while len(lines) < minlines + shift_size:
Martijn Pieters
graphmod: allow edges to end early...
r28601 lines.append(extra[:])
if shift_size:
empties = []
toshift = []
first_empty = extra.index(None)
for i, c in enumerate(extra[first_empty::2], first_empty // 2):
if c is None:
empties.append(i * 2)
else:
toshift.append(i * 2)
targets = list(range(first_empty, first_empty + len(toshift) * 2, 2))
positions = toshift[:]
for line in lines[-shift_size:]:
line[first_empty:] = [' '] * (len(line) - first_empty)
for i in range(len(positions)):
pos = positions[i] - 1
positions[i] = max(pos, targets[i])
line[pos] = '/' if pos > targets[i] else extra[toshift[i]]
Kyle Lippincott
log: respect graphshorten on terminal nodes (collapsing o-~ to just o~)...
r39322 map = {1: '|', 2: '~'} if not state['graphshorten'] else {1: '~'}
Martijn Pieters
graphmod: allow edges to end early...
r28601 for i, line in enumerate(lines):
if None not in line:
continue
line[:] = [c or map.get(i, ' ') for c in line]
# remove edges that ended
remove = [p for p, c in edgemap.items() if c is None]
for parent in remove:
del edgemap[parent]
seen.remove(parent)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 def asciistate():
"""returns the initial value for the "state" argument to ascii()"""
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 return {
'seen': [],
'edges': {},
'lastcoldiff': 0,
'lastindex': 0,
'styles': EDGES.copy(),
santiagopim
graphmod: shorten graph...
r28891 'graphshorten': False,
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 }
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
John Stiles
graph: add outputgraph() function, called by ascii() to print...
r38167 def outputgraph(ui, graph):
"""outputs an ASCII graph of a DAG
this is a helper function for 'ascii' below.
takes the following arguments:
- ui to write to
- graph data: list of { graph nodes/edges, text }
this function can be monkey-patched by extensions to alter graph display
without needing to mimic all of the edge-fixup logic in ascii()
"""
for (ln, logstr) in graph:
ui.write((ln + logstr).rstrip() + "\n")
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 def ascii(ui, state, type, char, text, coldata):
"""prints an ASCII graph of the DAG
takes the following arguments (one call per node in the graph):
- ui to write to
- Somewhere to keep the needed state in (init to asciistate())
- Column of the current node in the set of ongoing edges.
- Type indicator of node data, usually 'C' for changesets.
- Payload: (char, lines):
- Character to use as node's symbol.
- List of lines to display as the node's text.
- Edges; a list of (col, next_col) indicating the edges between
the current node and its parents.
- Number of columns (ongoing edges) in the current revision.
- The difference between the number of columns (ongoing edges)
in the next revision and the number of columns (ongoing edges)
in the current revision. That is: -1 means one column removed;
0 means no columns added or removed; 1 means one column added.
"""
idx, edges, ncols, coldiff = coldata
assert -2 < coldiff < 2
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600
edgemap, seen = state['edges'], state['seen']
# Be tolerant of history issues; make sure we have at least ncols + coldiff
# elements to work with. See test-glog.t for broken history test cases.
echars = [c for p in seen for c in (edgemap.get(p, '|'), ' ')]
echars.extend(('|', ' ') * max(ncols + coldiff - len(seen), 0))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if coldiff == -1:
# Transform
#
# | | | | | |
# o | | into o---+
# |X / |/ /
# | | | |
_fixlongrightedges(edges)
# add_padding_line says whether to rewrite
#
# | | | | | | | |
# | o---+ into | o---+
# | / / | | | # <--- padding line
# o | | | / /
# o | |
add_padding_line = (len(text) > 2 and coldiff == -1 and
[x for (x, y) in edges if x + 1 < y])
# fix_nodeline_tail says whether to rewrite
#
# | | o | | | | o | |
# | | |/ / | | |/ /
# | o | | into | o / / # <--- fixed nodeline tail
# | |/ / | |/ /
# o | | o | |
fix_nodeline_tail = len(text) <= 2 and not add_padding_line
# nodeline is the line containing the node character (typically o)
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 nodeline = echars[:idx * 2]
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 nodeline.extend([char, " "])
nodeline.extend(
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 _getnodelineedgestail(
echars, idx, state['lastindex'], ncols, coldiff,
state['lastcoldiff'], fix_nodeline_tail))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# shift_interline is the line containing the non-vertical
# edges between this entry and the next
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 shift_interline = echars[:idx * 2]
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(2 + coldiff):
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 shift_interline.append(' ')
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 count = ncols - idx - 1
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if coldiff == -1:
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(count):
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 shift_interline.extend(['/', ' '])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 elif coldiff == 0:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 shift_interline.extend(echars[(idx + 1) * 2:ncols * 2])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(count):
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 shift_interline.extend(['\\', ' '])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# draw edges from the current node to its parents
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 _drawedges(echars, edges, nodeline, shift_interline)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# lines is the list of all graph lines to print
lines = [nodeline]
if add_padding_line:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 lines.append(_getpaddingline(echars, idx, ncols, edges))
santiagopim
graphmod: shorten graph...
r28891
# If 'graphshorten' config, only draw shift_interline
# when there is any non vertical flow in graph.
if state['graphshorten']:
if any(c in '\/' for c in shift_interline if c):
lines.append(shift_interline)
# Else, no 'graphshorten' config so draw shift_interline.
else:
lines.append(shift_interline)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# make sure that there are as many graph lines as there are
# log strings
Martijn Pieters
graphmod: allow edges to end early...
r28601 extra_interline = echars[:(ncols + coldiff) * 2]
if len(lines) < len(text):
while len(lines) < len(text):
lines.append(extra_interline[:])
Kyle Lippincott
log: respect graphshorten on terminal nodes (collapsing o-~ to just o~)...
r39322 _drawendinglines(lines, extra_interline, edgemap, seen, state)
Martijn Pieters
graphmod: allow edges to end early...
r28601
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 while len(text) < len(lines):
text.append("")
Martijn Pieters
graphmod: partial edge styling...
r29134 if any(len(char) > 1 for char in edgemap.values()):
# limit drawing an edge to the first or last N lines of the current
# section the rest of the edge is drawn like a parent line.
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36198 parent = state['styles'][PARENT][-1:]
Martijn Pieters
graphmod: partial edge styling...
r29134 def _drawgp(char, i):
# should a grandparent character be drawn for this line?
if len(char) < 2:
return True
num = int(char[:-1])
# either skip first num lines or take last num lines, based on sign
return -num <= i if num < 0 else (len(lines) - i) <= num
for i, line in enumerate(lines):
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36198 line[:] = [c[-1:] if _drawgp(c, i) else parent for c in line]
Martijn Pieters
graphmod: update edgemap in-place...
r29184 edgemap.update(
(e, (c if len(c) < 2 else parent)) for e, c in edgemap.items())
Martijn Pieters
graphmod: partial edge styling...
r29134
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # print lines
indentation_level = max(ncols, ncols + coldiff)
John Stiles
graph: add outputgraph() function, called by ascii() to print...
r38167 lines = ["%-*s " % (2 * indentation_level, "".join(line)) for line in lines]
outputgraph(ui, zip(lines, text))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# ... and start over
Martijn Pieters
graphmod: refactor state handling...
r28375 state['lastcoldiff'] = coldiff
state['lastindex'] = idx