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