##// END OF EJS Templates
graphmod: rewrite graph config validation...
Matt Mackall -
r16131:6f236c8b default
parent child Browse files
Show More
@@ -1,159 +1,155
1 1 # Revision graph generator for Mercurial
2 2 #
3 3 # Copyright 2008 Dirkjan Ochtman <dirkjan@ochtman.nl>
4 4 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 """supports walking the history as DAGs suitable for graphical output
10 10
11 11 The most basic format we use is that of::
12 12
13 13 (id, type, data, [parentids])
14 14
15 15 The node and parent ids are arbitrary integers which identify a node in the
16 16 context of the graph returned. Type is a constant specifying the node type.
17 17 Data depends on type.
18 18 """
19 19
20 20 from mercurial.node import nullrev
21 import re
22 21
23 22 CHANGESET = 'C'
24 23
25 24 def dagwalker(repo, revs):
26 25 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
27 26
28 27 This generator function walks through revisions (which should be ordered
29 28 from bigger to lower). It returns a tuple for each node. The node and parent
30 29 ids are arbitrary integers which identify a node in the context of the graph
31 30 returned.
32 31 """
33 32 if not revs:
34 33 return
35 34
36 35 cl = repo.changelog
37 36 lowestrev = min(revs)
38 37 gpcache = {}
39 38
40 39 knownrevs = set(revs)
41 40 for rev in revs:
42 41 ctx = repo[rev]
43 42 parents = sorted(set([p.rev() for p in ctx.parents()
44 43 if p.rev() in knownrevs]))
45 44 mpars = [p.rev() for p in ctx.parents() if
46 45 p.rev() != nullrev and p.rev() not in parents]
47 46
48 47 for mpar in mpars:
49 48 gp = gpcache.get(mpar)
50 49 if gp is None:
51 50 gp = gpcache[mpar] = grandparent(cl, lowestrev, revs, mpar)
52 51 if not gp:
53 52 parents.append(mpar)
54 53 else:
55 54 parents.extend(g for g in gp if g not in parents)
56 55
57 56 yield (ctx.rev(), CHANGESET, ctx, parents)
58 57
59 58 def nodes(repo, nodes):
60 59 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
61 60
62 61 This generator function walks the given nodes. It only returns parents
63 62 that are in nodes, too.
64 63 """
65 64 include = set(nodes)
66 65 for node in nodes:
67 66 ctx = repo[node]
68 67 parents = set([p.rev() for p in ctx.parents() if p.node() in include])
69 68 yield (ctx.rev(), CHANGESET, ctx, sorted(parents))
70 69
71 70 def colored(dag, repo):
72 71 """annotates a DAG with colored edge information
73 72
74 73 For each DAG node this function emits tuples::
75 74
76 75 (id, type, data, (col, color), [(col, nextcol, color)])
77 76
78 77 with the following new elements:
79 78
80 79 - Tuple (col, color) with column and color index for the current node
81 80 - A list of tuples indicating the edges between the current node and its
82 81 parents.
83 82 """
84 83 seen = []
85 84 colors = {}
86 85 newcolor = 1
87 86 config = {}
88 87
89 88 for key, val in repo.ui.configitems('graph'):
90 if '.' not in key:
91 continue
89 if '.' in key:
92 90 branch, setting = key.rsplit('.', 1)
93 gdict = config.setdefault(branch, {})
94
95 91 # Validation
96 if ((setting == "width" and val.isdigit() and 0 < int(val) < 30) or
97 (setting == "color" and re.match('^[0-9a-fA-F]{6}$', val))):
98 gdict[setting] = val
99 else:
100 continue
92 if setting == "width" and val.isdigit():
93 config.setdefault(branch, {})[setting] = val
94 elif setting == "color" and val.isalnum():
95 config.setdefault(branch, {})[setting] = val
96
101 97
102 98 for (cur, type, data, parents) in dag:
103 99
104 100 # Compute seen and next
105 101 if cur not in seen:
106 102 seen.append(cur) # new head
107 103 colors[cur] = newcolor
108 104 newcolor += 1
109 105
110 106 col = seen.index(cur)
111 107 color = colors.pop(cur)
112 108 next = seen[:]
113 109
114 110 # Add parents to next
115 111 addparents = [p for p in parents if p not in next]
116 112 next[col:col + 1] = addparents
117 113
118 114 # Set colors for the parents
119 115 for i, p in enumerate(addparents):
120 116 if not i:
121 117 colors[p] = color
122 118 else:
123 119 colors[p] = newcolor
124 120 newcolor += 1
125 121
126 122 # Add edges to the graph
127 123 edges = []
128 124 for ecol, eid in enumerate(seen):
129 125 if eid in next:
130 126 edges.append((
131 127 ecol, next.index(eid), colors[eid],
132 128 config.get(repo[eid].branch(), None)))
133 129 elif eid == cur:
134 130 for p in parents:
135 131 edges.append((
136 132 ecol, next.index(p), color,
137 133 config.get(repo[p].branch(), None)))
138 134
139 135 # Yield and move on
140 136 yield (cur, type, data, (col, color), edges)
141 137 seen = next
142 138
143 139 def grandparent(cl, lowestrev, roots, head):
144 140 """Return all ancestors of head in roots which revision is
145 141 greater or equal to lowestrev.
146 142 """
147 143 pending = set([head])
148 144 seen = set()
149 145 kept = set()
150 146 llowestrev = max(nullrev, lowestrev)
151 147 while pending:
152 148 r = pending.pop()
153 149 if r >= llowestrev and r not in seen:
154 150 if r in roots:
155 151 kept.add(r)
156 152 else:
157 153 pending.update([p for p in cl.parentrevs(r)])
158 154 seen.add(r)
159 155 return sorted(kept)
General Comments 0
You need to be logged in to leave comments. Login now