##// END OF EJS Templates
graphlog: refactor common grapher code...
Peter Arrenbrecht -
r7370:7bc62ebe default
parent child Browse files
Show More
@@ -13,115 +13,76 b' from mercurial.commands import templateo'
13 13 from mercurial.i18n import _
14 14 from mercurial.node import nullrev
15 15 from mercurial.util import Abort, canonpath
16 from mercurial import util
17
18 def get_rev_parents(repo, rev):
19 return [x for x in repo.changelog.parentrevs(rev) if x != nullrev]
20
21 def revision_grapher(repo, start_rev, stop_rev):
22 """incremental revision grapher
23
24 This generator function walks through the revision history from
25 revision start_rev to revision stop_rev (which must be less than
26 or equal to start_rev) and for each revision emits tuples with the
27 following elements:
28
29 - Current revision.
30 - Current node.
31 - Column of the current node in the set of ongoing edges.
32 - Edges; a list of (col, next_col) indicating the edges between
33 the current node and its parents.
34 - Number of columns (ongoing edges) in the current revision.
35 - The difference between the number of columns (ongoing edges)
36 in the next revision and the number of columns (ongoing edges)
37 in the current revision. That is: -1 means one column removed;
38 0 means no columns added or removed; 1 means one column added.
39 """
40
41 assert start_rev >= stop_rev
42 curr_rev = start_rev
43 revs = []
44 while curr_rev >= stop_rev:
45 node = repo.changelog.node(curr_rev)
46
47 # Compute revs and next_revs.
48 if curr_rev not in revs:
49 # New head.
50 revs.append(curr_rev)
51 rev_index = revs.index(curr_rev)
52 next_revs = revs[:]
53 16
54 # Add parents to next_revs.
55 parents = get_rev_parents(repo, curr_rev)
56 parents_to_add = []
57 for parent in parents:
58 if parent not in next_revs:
59 parents_to_add.append(parent)
60 next_revs[rev_index:rev_index + 1] = util.sort(parents_to_add)
61
62 edges = []
63 for parent in parents:
64 edges.append((rev_index, next_revs.index(parent)))
65
66 n_columns_diff = len(next_revs) - len(revs)
67 yield (curr_rev, node, rev_index, edges, len(revs), n_columns_diff)
68
69 revs = next_revs
70 curr_rev -= 1
17 def revisions(repo, start, stop):
18 """cset DAG generator yielding (rev, node, [parents]) tuples
19
20 This generator function walks through the revision history from revision
21 start to revision stop (which must be less than or equal to start).
22 """
23 assert start >= stop
24 cur = start
25 while cur >= stop:
26 ctx = repo[cur]
27 parents = sorted(p.rev() for p in ctx.parents() if p.rev() != nullrev)
28 yield (ctx, parents)
29 cur -= 1
71 30
72 def filelog_grapher(repo, path, start_rev, stop_rev):
73 """incremental file log grapher
74
75 This generator function walks through the revision history of a
76 single file from revision start_rev to revision stop_rev (which must
77 be less than or equal to start_rev) and for each revision emits
78 tuples with the following elements:
79
80 - Current revision.
81 - Current node.
82 - Column of the current node in the set of ongoing edges.
83 - Edges; a list of (col, next_col) indicating the edges between
84 the current node and its parents.
85 - Number of columns (ongoing edges) in the current revision.
86 - The difference between the number of columns (ongoing edges)
87 in the next revision and the number of columns (ongoing edges)
88 in the current revision. That is: -1 means one column removed;
89 0 means no columns added or removed; 1 means one column added.
31 def filerevs(repo, path, start, stop):
32 """file cset DAG generator yielding (rev, node, [parents]) tuples
33
34 This generator function walks through the revision history of a single
35 file from revision start to revision stop (which must be less than or
36 equal to start).
90 37 """
91
92 assert start_rev >= stop_rev
93 revs = []
38 assert start >= stop
94 39 filerev = len(repo.file(path)) - 1
95 40 while filerev >= 0:
96 41 fctx = repo.filectx(path, fileid=filerev)
42 parents = [f.filerev() for f in fctx.parents() if f.path() == path]
43 parents.sort()
44 if fctx.rev() <= start:
45 yield (fctx, parents)
46 if fctx.rev() <= stop:
47 break
48 filerev -= 1
97 49
98 # Compute revs and next_revs.
99 if filerev not in revs:
100 revs.append(filerev)
101 rev_index = revs.index(filerev)
102 next_revs = revs[:]
50 def grapher(nodes):
51 """grapher for asciigraph on a list of nodes and their parents
52
53 nodes must generate tuples (node, parents, char, lines) where
54
55 - parents must generate the parents of node, in sorted order,
56 and max length 2,
57 - char is the char to print as the node symbol, and
58 - lines are the lines to display next to the node.
59 """
60 seen = []
61 for node, parents, char, lines in nodes:
62 if node not in seen:
63 seen.append(node)
64 nodeidx = seen.index(node)
103 65
104 # Add parents to next_revs.
105 parents = [f.filerev() for f in fctx.parents() if f.path() == path]
106 parents_to_add = []
66 knownparents = []
67 newparents = []
107 68 for parent in parents:
108 if parent not in next_revs:
109 parents_to_add.append(parent)
110 next_revs[rev_index:rev_index + 1] = util.sort(parents_to_add)
111
112 edges = []
113 for parent in parents:
114 edges.append((rev_index, next_revs.index(parent)))
69 if parent in seen:
70 knownparents.append(parent)
71 else:
72 newparents.append(parent)
115 73
116 changerev = fctx.linkrev()
117 if changerev <= start_rev:
118 node = repo.changelog.node(changerev)
119 n_columns_diff = len(next_revs) - len(revs)
120 yield (changerev, node, rev_index, edges, len(revs), n_columns_diff)
121 if changerev <= stop_rev:
122 break
123 revs = next_revs
124 filerev -= 1
74 ncols = len(seen)
75 nextseen = seen[:]
76 nextseen[nodeidx:nodeidx + 1] = newparents
77 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
78
79 if len(newparents) > 0:
80 edges.append((nodeidx, nodeidx))
81 if len(newparents) > 1:
82 edges.append((nodeidx, nodeidx + 1))
83 nmorecols = len(nextseen) - ncols
84 seen = nextseen
85 yield (char, lines, nodeidx, edges, ncols, nmorecols)
125 86
126 87 def fix_long_right_edges(edges):
127 88 for (i, (start, end)) in enumerate(edges):
@@ -307,33 +268,31 b' def graphlog(ui, repo, path=None, **opts'
307 268 """
308 269
309 270 limit = get_limit(opts["limit"])
310 (start_rev, stop_rev) = get_revs(repo, opts["rev"])
311 stop_rev = max(stop_rev, start_rev - limit + 1)
312 if start_rev == nullrev:
271 start, stop = get_revs(repo, opts["rev"])
272 stop = max(stop, start - limit + 1)
273 if start == nullrev:
313 274 return
275
314 276 if path:
315 277 path = canonpath(repo.root, os.getcwd(), path)
316 if path:
317 revgrapher = filelog_grapher(repo, path, start_rev, stop_rev)
278 if path: # could be reset in canonpath
279 revdag = filerevs(repo, path, start, stop)
318 280 else:
319 revgrapher = revision_grapher(repo, start_rev, stop_rev)
281 revdag = revisions(repo, start, stop)
320 282
321 283 repo_parents = repo.dirstate.parents()
322 cs_printer = show_changeset(ui, repo, opts)
323 def grapher():
324 for (rev, node, node_index, edges, n_columns, n_columns_diff) in revgrapher:
284 displayer = show_changeset(ui, repo, opts)
285 def graphabledag():
286 for (ctx, parents) in revdag:
325 287 # log_strings is the list of all log strings to draw alongside
326 288 # the graph.
327 289 ui.pushbuffer()
328 cs_printer.show(repo[rev])
290 displayer.show(ctx)
329 291 log_strings = ui.popbuffer().split("\n")[:-1]
330 if node in repo_parents:
331 node_ch = "@"
332 else:
333 node_ch = "o"
334 yield (node_ch, log_strings, node_index, edges, n_columns, n_columns_diff)
292 char = ctx.node() in repo_parents and '@' or 'o'
293 yield (ctx.rev(), parents, char, log_strings)
335 294
336 ascii(ui, grapher())
295 ascii(ui, grapher(graphabledag()))
337 296
338 297 cmdtable = {
339 298 "glog":
General Comments 0
You need to be logged in to leave comments. Login now