Mercurial > hg
annotate hgext/graphlog.py @ 5598:d534ba1c4eb4
separate the wire protocol commands from the user interface commands
author | Dirkjan Ochtman <dirkjan@ochtman.nl> |
---|---|
date | Mon, 03 Dec 2007 12:27:11 +0100 |
parents | 8d4fac0a9df7 |
children | 9ed100559851 |
rev | line source |
---|---|
4344 | 1 # ASCII graph log extension for Mercurial |
2 # | |
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net> | |
4516
96d8a56d4ef9
Removed trailing whitespace and tabs from python files
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4509
diff
changeset
|
4 # |
4344 | 5 # This software may be used and distributed according to the terms of |
6 # the GNU General Public License, incorporated herein by reference. | |
7 | |
8 import sys | |
9 from mercurial.cmdutil import revrange, show_changeset | |
10 from mercurial.i18n import _ | |
11 from mercurial.node import nullid, nullrev | |
12 from mercurial.util import Abort | |
13 | |
14 def revision_grapher(repo, start_rev, stop_rev): | |
15 """incremental revision grapher | |
16 | |
17 This generator function walks through the revision history from | |
18 revision start_rev to revision stop_rev (which must be less than | |
19 or equal to start_rev) and for each revision emits tuples with the | |
20 following elements: | |
21 | |
22 - Current revision. | |
23 - Current node. | |
24 - Column of the current node in the set of ongoing edges. | |
25 - Edges; a list of (col, next_col) indicating the edges between | |
26 the current node and its parents. | |
27 - Number of columns (ongoing edges) in the current revision. | |
28 - The difference between the number of columns (ongoing edges) | |
29 in the next revision and the number of columns (ongoing edges) | |
30 in the current revision. That is: -1 means one column removed; | |
31 0 means no columns added or removed; 1 means one column added. | |
32 """ | |
33 | |
34 assert start_rev >= stop_rev | |
35 curr_rev = start_rev | |
36 revs = [] | |
37 while curr_rev >= stop_rev: | |
38 node = repo.changelog.node(curr_rev) | |
39 | |
40 # Compute revs and next_revs. | |
41 if curr_rev not in revs: | |
42 # New head. | |
43 revs.append(curr_rev) | |
44 rev_index = revs.index(curr_rev) | |
45 next_revs = revs[:] | |
46 | |
47 # Add parents to next_revs. | |
48 parents = get_rev_parents(repo, curr_rev) | |
49 parents_to_add = [] | |
50 for parent in parents: | |
51 if parent not in next_revs: | |
52 parents_to_add.append(parent) | |
53 parents_to_add.sort() | |
54 next_revs[rev_index:rev_index + 1] = parents_to_add | |
55 | |
56 edges = [] | |
57 for parent in parents: | |
58 edges.append((rev_index, next_revs.index(parent))) | |
59 | |
60 n_columns_diff = len(next_revs) - len(revs) | |
61 yield (curr_rev, node, rev_index, edges, len(revs), n_columns_diff) | |
62 | |
63 revs = next_revs | |
64 curr_rev -= 1 | |
65 | |
66 def get_rev_parents(repo, rev): | |
67 return [x for x in repo.changelog.parentrevs(rev) if x != nullrev] | |
68 | |
69 def fix_long_right_edges(edges): | |
70 for (i, (start, end)) in enumerate(edges): | |
71 if end > start: | |
72 edges[i] = (start, end + 1) | |
73 | |
74 def draw_edges(edges, nodeline, interline): | |
75 for (start, end) in edges: | |
76 if start == end + 1: | |
77 interline[2 * end + 1] = "/" | |
78 elif start == end - 1: | |
79 interline[2 * start + 1] = "\\" | |
80 elif start == end: | |
81 interline[2 * start] = "|" | |
82 else: | |
83 nodeline[2 * end] = "+" | |
84 if start > end: | |
85 (start, end) = (end,start) | |
86 for i in range(2 * start + 1, 2 * end): | |
87 if nodeline[i] != "+": | |
88 nodeline[i] = "-" | |
89 | |
90 def format_line(line, level, logstr): | |
91 text = "%-*s %s" % (2 * level, "".join(line), logstr) | |
92 return "%s\n" % text.rstrip() | |
93 | |
94 def get_nodeline_edges_tail( | |
95 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail): | |
96 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0: | |
97 # Still going in the same non-vertical direction. | |
98 if n_columns_diff == -1: | |
99 start = max(node_index + 1, p_node_index) | |
100 tail = ["|", " "] * (start - node_index - 1) | |
101 tail.extend(["/", " "] * (n_columns - start)) | |
102 return tail | |
103 else: | |
104 return ["\\", " "] * (n_columns - node_index - 1) | |
105 else: | |
106 return ["|", " "] * (n_columns - node_index - 1) | |
107 | |
108 def get_padding_line(ni, n_columns, edges): | |
109 line = [] | |
110 line.extend(["|", " "] * ni) | |
111 if (ni, ni - 1) in edges or (ni, ni) in edges: | |
112 # (ni, ni - 1) (ni, ni) | |
113 # | | | | | | | | | |
114 # +---o | | o---+ | |
115 # | | c | | c | | | |
116 # | |/ / | |/ / | |
117 # | | | | | | | |
118 c = "|" | |
119 else: | |
120 c = " " | |
121 line.extend([c, " "]) | |
122 line.extend(["|", " "] * (n_columns - ni - 1)) | |
123 return line | |
124 | |
125 def get_limit(limit_opt): | |
126 if limit_opt: | |
127 try: | |
128 limit = int(limit_opt) | |
129 except ValueError: | |
130 raise Abort(_("limit must be a positive integer")) | |
131 if limit <= 0: | |
132 raise Abort(_("limit must be positive")) | |
133 else: | |
134 limit = sys.maxint | |
135 return limit | |
136 | |
137 def get_revs(repo, rev_opt): | |
138 if rev_opt: | |
139 revs = revrange(repo, rev_opt) | |
140 return (max(revs), min(revs)) | |
141 else: | |
142 return (repo.changelog.count() - 1, 0) | |
143 | |
4735
8d4fac0a9df7
Make hg glog abort if unused arguments are given.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4730
diff
changeset
|
144 def graphlog(ui, repo, **opts): |
4344 | 145 """show revision history alongside an ASCII revision graph |
146 | |
147 Print a revision history alongside a revision graph drawn with | |
148 ASCII characters. | |
149 | |
4583
11cf78983961
Reverted changesets 9d1380e5c8c5 and 1d46169ec197: show @ as glog parent again.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4516
diff
changeset
|
150 Nodes printed as an @ character are parents of the working |
4344 | 151 directory. |
152 """ | |
153 | |
154 limit = get_limit(opts["limit"]) | |
155 (start_rev, stop_rev) = get_revs(repo, opts["rev"]) | |
156 stop_rev = max(stop_rev, start_rev - limit + 1) | |
157 if start_rev == nullrev: | |
158 return | |
159 cs_printer = show_changeset(ui, repo, opts) | |
160 grapher = revision_grapher(repo, start_rev, stop_rev) | |
161 repo_parents = repo.dirstate.parents() | |
162 prev_n_columns_diff = 0 | |
163 prev_node_index = 0 | |
164 | |
165 for (rev, node, node_index, edges, n_columns, n_columns_diff) in grapher: | |
166 # log_strings is the list of all log strings to draw alongside | |
167 # the graph. | |
168 ui.pushbuffer() | |
169 cs_printer.show(rev, node) | |
170 log_strings = ui.popbuffer().split("\n")[:-1] | |
171 | |
172 if n_columns_diff == -1: | |
173 # Transform | |
174 # | |
175 # | | | | | | | |
176 # o | | into o---+ | |
177 # |X / |/ / | |
178 # | | | | | |
179 fix_long_right_edges(edges) | |
180 | |
181 # add_padding_line says whether to rewrite | |
182 # | |
183 # | | | | | | | | | |
184 # | o---+ into | o---+ | |
185 # | / / | | | # <--- padding line | |
186 # o | | | / / | |
187 # o | | | |
4633
ff7253a0d1da
Cleanup of whitespace, indentation and line continuation.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4583
diff
changeset
|
188 add_padding_line = (len(log_strings) > 2 and |
ff7253a0d1da
Cleanup of whitespace, indentation and line continuation.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4583
diff
changeset
|
189 n_columns_diff == -1 and |
ff7253a0d1da
Cleanup of whitespace, indentation and line continuation.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4583
diff
changeset
|
190 [x for (x, y) in edges if x + 1 < y]) |
4344 | 191 |
192 # fix_nodeline_tail says whether to rewrite | |
193 # | |
194 # | | o | | | | o | | | |
195 # | | |/ / | | |/ / | |
196 # | o | | into | o / / # <--- fixed nodeline tail | |
197 # | |/ / | |/ / | |
198 # o | | o | | | |
199 fix_nodeline_tail = len(log_strings) <= 2 and not add_padding_line | |
200 | |
4583
11cf78983961
Reverted changesets 9d1380e5c8c5 and 1d46169ec197: show @ as glog parent again.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4516
diff
changeset
|
201 # nodeline is the line containing the node character (@ or o). |
4344 | 202 nodeline = ["|", " "] * node_index |
203 if node in repo_parents: | |
4583
11cf78983961
Reverted changesets 9d1380e5c8c5 and 1d46169ec197: show @ as glog parent again.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4516
diff
changeset
|
204 node_ch = "@" |
4344 | 205 else: |
206 node_ch = "o" | |
207 nodeline.extend([node_ch, " "]) | |
208 | |
209 nodeline.extend( | |
210 get_nodeline_edges_tail( | |
211 node_index, prev_node_index, n_columns, n_columns_diff, | |
212 prev_n_columns_diff, fix_nodeline_tail)) | |
213 | |
214 # shift_interline is the line containing the non-vertical | |
215 # edges between this entry and the next. | |
216 shift_interline = ["|", " "] * node_index | |
217 if n_columns_diff == -1: | |
218 n_spaces = 1 | |
219 edge_ch = "/" | |
220 elif n_columns_diff == 0: | |
221 n_spaces = 2 | |
222 edge_ch = "|" | |
223 else: | |
224 n_spaces = 3 | |
225 edge_ch = "\\" | |
226 shift_interline.extend(n_spaces * [" "]) | |
227 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1)) | |
228 | |
229 # Draw edges from the current node to its parents. | |
230 draw_edges(edges, nodeline, shift_interline) | |
231 | |
232 # lines is the list of all graph lines to print. | |
233 lines = [nodeline] | |
234 if add_padding_line: | |
235 lines.append(get_padding_line(node_index, n_columns, edges)) | |
236 lines.append(shift_interline) | |
237 | |
238 # Make sure that there are as many graph lines as there are | |
239 # log strings. | |
240 while len(log_strings) < len(lines): | |
241 log_strings.append("") | |
242 if len(lines) < len(log_strings): | |
243 extra_interline = ["|", " "] * (n_columns + n_columns_diff) | |
244 while len(lines) < len(log_strings): | |
245 lines.append(extra_interline) | |
246 | |
247 # Print lines. | |
248 indentation_level = max(n_columns, n_columns + n_columns_diff) | |
249 for (line, logstr) in zip(lines, log_strings): | |
250 ui.write(format_line(line, indentation_level, logstr)) | |
251 | |
252 # ...and start over. | |
253 prev_node_index = node_index | |
254 prev_n_columns_diff = n_columns_diff | |
255 | |
256 cmdtable = { | |
257 "glog": | |
4730
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
258 (graphlog, |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
259 [('l', 'limit', '', _('limit number of changes displayed')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
260 ('p', 'patch', False, _('show patch')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
261 ('r', 'rev', [], _('show the specified revision or range')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
262 ('', 'style', '', _('display using template map file')), |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
263 ('', 'template', '', _('display with template'))], |
eadfaa9ec487
Updated command tables in commands.py and hgext extensions.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4633
diff
changeset
|
264 _('hg glog [OPTION]...')), |
4344 | 265 } |