135
|
1 # Revision graph generator for Mercurial
|
|
2 #
|
|
3 # Copyright 2008 Dirkjan Ochtman <dirkjan@ochtman.nl>
|
|
4 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
|
|
5 #
|
|
6 # This software may be used and distributed according to the terms of the
|
|
7 # GNU General Public License version 2, incorporated herein by reference.
|
|
8
|
|
9 """supports walking the history as DAGs suitable for graphical output
|
|
10
|
|
11 The most basic format we use is that of::
|
|
12
|
|
13 (id, type, data, [parentids])
|
|
14
|
|
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.
|
|
17 Data depends on type.
|
|
18 """
|
|
19
|
|
20 from mercurial.node import nullrev
|
|
21
|
|
22 CHANGESET = 'C'
|
|
23
|
|
24 def revisions(repo, start, stop):
|
|
25 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
|
|
26
|
|
27 This generator function walks through the revision history from revision
|
|
28 start to revision stop (which must be less than or equal to start). It
|
|
29 returns a tuple for each node. The node and parent ids are arbitrary
|
|
30 integers which identify a node in the context of the graph returned.
|
|
31 """
|
|
32 cur = start
|
|
33 while cur >= stop:
|
|
34 ctx = repo[cur]
|
|
35 parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev]
|
|
36 yield (cur, CHANGESET, ctx, sorted(parents))
|
|
37 cur -= 1
|
|
38
|
|
39 def filerevs(repo, path, start, stop):
|
|
40 """file cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
|
|
41
|
|
42 This generator function walks through the revision history of a single
|
|
43 file from revision start down to revision stop.
|
|
44 """
|
|
45 filerev = len(repo.file(path)) - 1
|
|
46 while filerev >= 0:
|
|
47 fctx = repo.filectx(path, fileid=filerev)
|
|
48 parents = [f.linkrev() for f in fctx.parents() if f.path() == path]
|
|
49 rev = fctx.rev()
|
|
50 if rev <= start:
|
|
51 yield (rev, CHANGESET, fctx, sorted(parents))
|
|
52 if rev <= stop:
|
|
53 break
|
|
54 filerev -= 1
|
|
55
|
|
56 def nodes(repo, nodes):
|
|
57 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
|
|
58
|
|
59 This generator function walks the given nodes. It only returns parents
|
|
60 that are in nodes, too.
|
|
61 """
|
|
62 include = set(nodes)
|
|
63 for node in nodes:
|
|
64 ctx = repo[node]
|
|
65 parents = [p.rev() for p in ctx.parents() if p.node() in include]
|
|
66 yield (ctx.rev(), CHANGESET, ctx, sorted(parents))
|
|
67
|
|
68 def colored(dag):
|
|
69 """annotates a DAG with colored edge information
|
|
70
|
|
71 For each DAG node this function emits tuples::
|
|
72
|
|
73 (id, type, data, (col, color), [(col, nextcol, color)])
|
|
74
|
|
75 with the following new elements:
|
|
76
|
|
77 - Tuple (col, color) with column and color index for the current node
|
|
78 - A list of tuples indicating the edges between the current node and its
|
|
79 parents.
|
|
80 """
|
|
81 seen = []
|
|
82 colors = {}
|
|
83 newcolor = 1
|
|
84 for (cur, type, data, parents) in dag:
|
|
85
|
|
86 # Compute seen and next
|
|
87 if cur not in seen:
|
|
88 seen.append(cur) # new head
|
|
89 colors[cur] = newcolor
|
|
90 newcolor += 1
|
|
91
|
|
92 col = seen.index(cur)
|
|
93 color = colors.pop(cur)
|
|
94 next = seen[:]
|
|
95
|
|
96 # Add parents to next
|
|
97 addparents = [p for p in parents if p not in next]
|
|
98 next[col:col + 1] = addparents
|
|
99
|
|
100 # Set colors for the parents
|
|
101 for i, p in enumerate(addparents):
|
|
102 if not i:
|
|
103 colors[p] = color
|
|
104 else:
|
|
105 colors[p] = newcolor
|
|
106 newcolor += 1
|
|
107
|
|
108 # Add edges to the graph
|
|
109 edges = []
|
|
110 for ecol, eid in enumerate(seen):
|
|
111 if eid in next:
|
|
112 edges.append((ecol, next.index(eid), colors[eid]))
|
|
113 elif eid == cur:
|
|
114 for p in parents:
|
|
115 edges.append((ecol, next.index(p), colors[p]))
|
|
116
|
|
117 # Yield and move on
|
|
118 yield (cur, type, data, (col, color), edges)
|
|
119 seen = next
|