##// END OF EJS Templates
graphlog: make some comment lines more like others in punctuation
Peter Arrenbrecht -
r7323:1c9f7aa7 default
parent child Browse files
Show More
@@ -1,326 +1,325 b''
1 1 # ASCII graph log extension for Mercurial
2 2 #
3 3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of
6 6 # the GNU General Public License, incorporated herein by reference.
7 7 '''show revision graphs in terminal windows'''
8 8
9 9 import os
10 10 import sys
11 11 from mercurial.cmdutil import revrange, show_changeset
12 12 from mercurial.commands import templateopts
13 13 from mercurial.i18n import _
14 14 from mercurial.node import nullrev
15 15 from mercurial.util import Abort, canonpath
16 16 from mercurial import util
17 17
18 18 def revision_grapher(repo, start_rev, stop_rev):
19 19 """incremental revision grapher
20 20
21 21 This generator function walks through the revision history from
22 22 revision start_rev to revision stop_rev (which must be less than
23 23 or equal to start_rev) and for each revision emits tuples with the
24 24 following elements:
25 25
26 26 - Current revision.
27 27 - Current node.
28 28 - Column of the current node in the set of ongoing edges.
29 29 - Edges; a list of (col, next_col) indicating the edges between
30 30 the current node and its parents.
31 31 - Number of columns (ongoing edges) in the current revision.
32 32 - The difference between the number of columns (ongoing edges)
33 33 in the next revision and the number of columns (ongoing edges)
34 34 in the current revision. That is: -1 means one column removed;
35 35 0 means no columns added or removed; 1 means one column added.
36 36 """
37 37
38 38 assert start_rev >= stop_rev
39 39 curr_rev = start_rev
40 40 revs = []
41 41 while curr_rev >= stop_rev:
42 42 node = repo.changelog.node(curr_rev)
43 43
44 44 # Compute revs and next_revs.
45 45 if curr_rev not in revs:
46 46 # New head.
47 47 revs.append(curr_rev)
48 48 rev_index = revs.index(curr_rev)
49 49 next_revs = revs[:]
50 50
51 51 # Add parents to next_revs.
52 52 parents = get_rev_parents(repo, curr_rev)
53 53 parents_to_add = []
54 54 for parent in parents:
55 55 if parent not in next_revs:
56 56 parents_to_add.append(parent)
57 57 next_revs[rev_index:rev_index + 1] = util.sort(parents_to_add)
58 58
59 59 edges = []
60 60 for parent in parents:
61 61 edges.append((rev_index, next_revs.index(parent)))
62 62
63 63 n_columns_diff = len(next_revs) - len(revs)
64 64 yield (curr_rev, node, rev_index, edges, len(revs), n_columns_diff)
65 65
66 66 revs = next_revs
67 67 curr_rev -= 1
68 68
69 69 def filelog_grapher(repo, path, start_rev, stop_rev):
70 70 """incremental file log grapher
71 71
72 72 This generator function walks through the revision history of a
73 73 single file from revision start_rev to revision stop_rev (which must
74 74 be less than or equal to start_rev) and for each revision emits
75 75 tuples with the following elements:
76 76
77 77 - Current revision.
78 78 - Current node.
79 79 - Column of the current node in the set of ongoing edges.
80 80 - Edges; a list of (col, next_col) indicating the edges between
81 81 the current node and its parents.
82 82 - Number of columns (ongoing edges) in the current revision.
83 83 - The difference between the number of columns (ongoing edges)
84 84 in the next revision and the number of columns (ongoing edges)
85 85 in the current revision. That is: -1 means one column removed;
86 86 0 means no columns added or removed; 1 means one column added.
87 87 """
88 88
89 89 assert start_rev >= stop_rev
90 90 revs = []
91 91 filerev = len(repo.file(path)) - 1
92 92 while filerev >= 0:
93 93 fctx = repo.filectx(path, fileid=filerev)
94 94
95 95 # Compute revs and next_revs.
96 96 if filerev not in revs:
97 97 revs.append(filerev)
98 98 rev_index = revs.index(filerev)
99 99 next_revs = revs[:]
100 100
101 101 # Add parents to next_revs.
102 102 parents = [f.filerev() for f in fctx.parents() if f.path() == path]
103 103 parents_to_add = []
104 104 for parent in parents:
105 105 if parent not in next_revs:
106 106 parents_to_add.append(parent)
107 107 next_revs[rev_index:rev_index + 1] = util.sort(parents_to_add)
108 108
109 109 edges = []
110 110 for parent in parents:
111 111 edges.append((rev_index, next_revs.index(parent)))
112 112
113 113 changerev = fctx.linkrev()
114 114 if changerev <= start_rev:
115 115 node = repo.changelog.node(changerev)
116 116 n_columns_diff = len(next_revs) - len(revs)
117 117 yield (changerev, node, rev_index, edges, len(revs), n_columns_diff)
118 118 if changerev <= stop_rev:
119 119 break
120 120 revs = next_revs
121 121 filerev -= 1
122 122
123 123 def get_rev_parents(repo, rev):
124 124 return [x for x in repo.changelog.parentrevs(rev) if x != nullrev]
125 125
126 126 def fix_long_right_edges(edges):
127 127 for (i, (start, end)) in enumerate(edges):
128 128 if end > start:
129 129 edges[i] = (start, end + 1)
130 130
131 131 def draw_edges(edges, nodeline, interline):
132 132 for (start, end) in edges:
133 133 if start == end + 1:
134 134 interline[2 * end + 1] = "/"
135 135 elif start == end - 1:
136 136 interline[2 * start + 1] = "\\"
137 137 elif start == end:
138 138 interline[2 * start] = "|"
139 139 else:
140 140 nodeline[2 * end] = "+"
141 141 if start > end:
142 142 (start, end) = (end,start)
143 143 for i in range(2 * start + 1, 2 * end):
144 144 if nodeline[i] != "+":
145 145 nodeline[i] = "-"
146 146
147 147 def format_line(line, level, logstr):
148 148 text = "%-*s %s" % (2 * level, "".join(line), logstr)
149 149 return "%s\n" % text.rstrip()
150 150
151 151 def get_nodeline_edges_tail(
152 152 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
153 153 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
154 154 # Still going in the same non-vertical direction.
155 155 if n_columns_diff == -1:
156 156 start = max(node_index + 1, p_node_index)
157 157 tail = ["|", " "] * (start - node_index - 1)
158 158 tail.extend(["/", " "] * (n_columns - start))
159 159 return tail
160 160 else:
161 161 return ["\\", " "] * (n_columns - node_index - 1)
162 162 else:
163 163 return ["|", " "] * (n_columns - node_index - 1)
164 164
165 165 def get_padding_line(ni, n_columns, edges):
166 166 line = []
167 167 line.extend(["|", " "] * ni)
168 168 if (ni, ni - 1) in edges or (ni, ni) in edges:
169 169 # (ni, ni - 1) (ni, ni)
170 170 # | | | | | | | |
171 171 # +---o | | o---+
172 172 # | | c | | c | |
173 173 # | |/ / | |/ /
174 174 # | | | | | |
175 175 c = "|"
176 176 else:
177 177 c = " "
178 178 line.extend([c, " "])
179 179 line.extend(["|", " "] * (n_columns - ni - 1))
180 180 return line
181 181
182 182 def get_limit(limit_opt):
183 183 if limit_opt:
184 184 try:
185 185 limit = int(limit_opt)
186 186 except ValueError:
187 187 raise Abort(_("limit must be a positive integer"))
188 188 if limit <= 0:
189 189 raise Abort(_("limit must be positive"))
190 190 else:
191 191 limit = sys.maxint
192 192 return limit
193 193
194 194 def get_revs(repo, rev_opt):
195 195 if rev_opt:
196 196 revs = revrange(repo, rev_opt)
197 197 return (max(revs), min(revs))
198 198 else:
199 199 return (len(repo) - 1, 0)
200 200
201 201 def graphlog(ui, repo, path=None, **opts):
202 202 """show revision history alongside an ASCII revision graph
203 203
204 204 Print a revision history alongside a revision graph drawn with
205 205 ASCII characters.
206 206
207 207 Nodes printed as an @ character are parents of the working
208 208 directory.
209 209 """
210 210
211 211 limit = get_limit(opts["limit"])
212 212 (start_rev, stop_rev) = get_revs(repo, opts["rev"])
213 213 stop_rev = max(stop_rev, start_rev - limit + 1)
214 214 if start_rev == nullrev:
215 215 return
216 216 cs_printer = show_changeset(ui, repo, opts)
217 217 if path:
218 218 path = canonpath(repo.root, os.getcwd(), path)
219 219 if path:
220 220 grapher = filelog_grapher(repo, path, start_rev, stop_rev)
221 221 else:
222 222 grapher = revision_grapher(repo, start_rev, stop_rev)
223 223 repo_parents = repo.dirstate.parents()
224 224 prev_n_columns_diff = 0
225 225 prev_node_index = 0
226 226
227 227 for (rev, node, node_index, edges, n_columns, n_columns_diff) in grapher:
228 # log_strings is the list of all log strings to draw alongside
229 # the graph.
228 # log_strings is the list of all log strings to draw alongside the graph
230 229 ui.pushbuffer()
231 230 cs_printer.show(rev, node)
232 231 log_strings = ui.popbuffer().split("\n")[:-1]
233 232
234 233 if n_columns_diff == -1:
235 234 # Transform
236 235 #
237 236 # | | | | | |
238 237 # o | | into o---+
239 238 # |X / |/ /
240 239 # | | | |
241 240 fix_long_right_edges(edges)
242 241
243 242 # add_padding_line says whether to rewrite
244 243 #
245 244 # | | | | | | | |
246 245 # | o---+ into | o---+
247 246 # | / / | | | # <--- padding line
248 247 # o | | | / /
249 248 # o | |
250 249 add_padding_line = (len(log_strings) > 2 and
251 250 n_columns_diff == -1 and
252 251 [x for (x, y) in edges if x + 1 < y])
253 252
254 253 # fix_nodeline_tail says whether to rewrite
255 254 #
256 255 # | | o | | | | o | |
257 256 # | | |/ / | | |/ /
258 257 # | o | | into | o / / # <--- fixed nodeline tail
259 258 # | |/ / | |/ /
260 259 # o | | o | |
261 260 fix_nodeline_tail = len(log_strings) <= 2 and not add_padding_line
262 261
263 # nodeline is the line containing the node character (@ or o).
262 # nodeline is the line containing the node character (typically o)
264 263 nodeline = ["|", " "] * node_index
265 264 if node in repo_parents:
266 265 node_ch = "@"
267 266 else:
268 267 node_ch = "o"
269 268 nodeline.extend([node_ch, " "])
270 269
271 270 nodeline.extend(
272 271 get_nodeline_edges_tail(
273 272 node_index, prev_node_index, n_columns, n_columns_diff,
274 273 prev_n_columns_diff, fix_nodeline_tail))
275 274
276 275 # shift_interline is the line containing the non-vertical
277 # edges between this entry and the next.
276 # edges between this entry and the next
278 277 shift_interline = ["|", " "] * node_index
279 278 if n_columns_diff == -1:
280 279 n_spaces = 1
281 280 edge_ch = "/"
282 281 elif n_columns_diff == 0:
283 282 n_spaces = 2
284 283 edge_ch = "|"
285 284 else:
286 285 n_spaces = 3
287 286 edge_ch = "\\"
288 287 shift_interline.extend(n_spaces * [" "])
289 288 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
290 289
291 # Draw edges from the current node to its parents.
290 # draw edges from the current node to its parents
292 291 draw_edges(edges, nodeline, shift_interline)
293 292
294 # lines is the list of all graph lines to print.
293 # lines is the list of all graph lines to print
295 294 lines = [nodeline]
296 295 if add_padding_line:
297 296 lines.append(get_padding_line(node_index, n_columns, edges))
298 297 lines.append(shift_interline)
299 298
300 # Make sure that there are as many graph lines as there are
301 # log strings.
299 # make sure that there are as many graph lines as there are
300 # log strings
302 301 while len(log_strings) < len(lines):
303 302 log_strings.append("")
304 303 if len(lines) < len(log_strings):
305 304 extra_interline = ["|", " "] * (n_columns + n_columns_diff)
306 305 while len(lines) < len(log_strings):
307 306 lines.append(extra_interline)
308 307
309 # Print lines.
308 # print lines
310 309 indentation_level = max(n_columns, n_columns + n_columns_diff)
311 310 for (line, logstr) in zip(lines, log_strings):
312 311 ui.write(format_line(line, indentation_level, logstr))
313 312
314 # ...and start over.
313 # ... and start over
315 314 prev_node_index = node_index
316 315 prev_n_columns_diff = n_columns_diff
317 316
318 317 cmdtable = {
319 318 "glog":
320 319 (graphlog,
321 320 [('l', 'limit', '', _('limit number of changes displayed')),
322 321 ('p', 'patch', False, _('show patch')),
323 322 ('r', 'rev', [], _('show the specified revision or range')),
324 323 ] + templateopts,
325 324 _('hg glog [OPTION]... [FILE]')),
326 325 }
General Comments 0
You need to be logged in to leave comments. Login now