##// END OF EJS Templates
Add back elipsis to [OPTION] that was stolen by 56e8a54bf71d
Thomas Arendsen Hein -
r5940:7b222815 default
parent child Browse files
Show More
@@ -1,326 +1,326 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
8 8 import os
9 9 import sys
10 10 from mercurial.cmdutil import revrange, show_changeset
11 11 from mercurial.i18n import _
12 12 from mercurial.node import nullid, nullrev
13 13 from mercurial.util import Abort, canonpath
14 14
15 15 def revision_grapher(repo, start_rev, stop_rev):
16 16 """incremental revision grapher
17 17
18 18 This generator function walks through the revision history from
19 19 revision start_rev to revision stop_rev (which must be less than
20 20 or equal to start_rev) and for each revision emits tuples with the
21 21 following elements:
22 22
23 23 - Current revision.
24 24 - Current node.
25 25 - Column of the current node in the set of ongoing edges.
26 26 - Edges; a list of (col, next_col) indicating the edges between
27 27 the current node and its parents.
28 28 - Number of columns (ongoing edges) in the current revision.
29 29 - The difference between the number of columns (ongoing edges)
30 30 in the next revision and the number of columns (ongoing edges)
31 31 in the current revision. That is: -1 means one column removed;
32 32 0 means no columns added or removed; 1 means one column added.
33 33 """
34 34
35 35 assert start_rev >= stop_rev
36 36 curr_rev = start_rev
37 37 revs = []
38 38 while curr_rev >= stop_rev:
39 39 node = repo.changelog.node(curr_rev)
40 40
41 41 # Compute revs and next_revs.
42 42 if curr_rev not in revs:
43 43 # New head.
44 44 revs.append(curr_rev)
45 45 rev_index = revs.index(curr_rev)
46 46 next_revs = revs[:]
47 47
48 48 # Add parents to next_revs.
49 49 parents = get_rev_parents(repo, curr_rev)
50 50 parents_to_add = []
51 51 for parent in parents:
52 52 if parent not in next_revs:
53 53 parents_to_add.append(parent)
54 54 parents_to_add.sort()
55 55 next_revs[rev_index:rev_index + 1] = parents_to_add
56 56
57 57 edges = []
58 58 for parent in parents:
59 59 edges.append((rev_index, next_revs.index(parent)))
60 60
61 61 n_columns_diff = len(next_revs) - len(revs)
62 62 yield (curr_rev, node, rev_index, edges, len(revs), n_columns_diff)
63 63
64 64 revs = next_revs
65 65 curr_rev -= 1
66 66
67 67 def filelog_grapher(repo, path, start_rev, stop_rev):
68 68 """incremental file log grapher
69 69
70 70 This generator function walks through the revision history of a
71 71 single file from revision start_rev to revision stop_rev (which must
72 72 be less than or equal to start_rev) and for each revision emits
73 73 tuples with the following elements:
74 74
75 75 - Current revision.
76 76 - Current node.
77 77 - Column of the current node in the set of ongoing edges.
78 78 - Edges; a list of (col, next_col) indicating the edges between
79 79 the current node and its parents.
80 80 - Number of columns (ongoing edges) in the current revision.
81 81 - The difference between the number of columns (ongoing edges)
82 82 in the next revision and the number of columns (ongoing edges)
83 83 in the current revision. That is: -1 means one column removed;
84 84 0 means no columns added or removed; 1 means one column added.
85 85 """
86 86
87 87 assert start_rev >= stop_rev
88 88 curr_rev = start_rev
89 89 revs = []
90 90 filerev = repo.file(path).count() - 1
91 91 while filerev >= 0:
92 92 fctx = repo.filectx(path, fileid=filerev)
93 93
94 94 # Compute revs and next_revs.
95 95 if filerev not in revs:
96 96 revs.append(filerev)
97 97 rev_index = revs.index(filerev)
98 98 next_revs = revs[:]
99 99
100 100 # Add parents to next_revs.
101 101 parents = [f.filerev() for f in fctx.parents()]
102 102 parents_to_add = []
103 103 for parent in parents:
104 104 if parent not in next_revs:
105 105 parents_to_add.append(parent)
106 106 parents_to_add.sort()
107 107 next_revs[rev_index:rev_index + 1] = 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 (repo.changelog.count() - 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 cpath = canonpath(repo.root, os.getcwd(), path)
219 219 grapher = filelog_grapher(repo, cpath, start_rev, stop_rev)
220 220 else:
221 221 grapher = revision_grapher(repo, start_rev, stop_rev)
222 222 repo_parents = repo.dirstate.parents()
223 223 prev_n_columns_diff = 0
224 224 prev_node_index = 0
225 225
226 226 for (rev, node, node_index, edges, n_columns, n_columns_diff) in grapher:
227 227 # log_strings is the list of all log strings to draw alongside
228 228 # the graph.
229 229 ui.pushbuffer()
230 230 cs_printer.show(rev, node)
231 231 log_strings = ui.popbuffer().split("\n")[:-1]
232 232
233 233 if n_columns_diff == -1:
234 234 # Transform
235 235 #
236 236 # | | | | | |
237 237 # o | | into o---+
238 238 # |X / |/ /
239 239 # | | | |
240 240 fix_long_right_edges(edges)
241 241
242 242 # add_padding_line says whether to rewrite
243 243 #
244 244 # | | | | | | | |
245 245 # | o---+ into | o---+
246 246 # | / / | | | # <--- padding line
247 247 # o | | | / /
248 248 # o | |
249 249 add_padding_line = (len(log_strings) > 2 and
250 250 n_columns_diff == -1 and
251 251 [x for (x, y) in edges if x + 1 < y])
252 252
253 253 # fix_nodeline_tail says whether to rewrite
254 254 #
255 255 # | | o | | | | o | |
256 256 # | | |/ / | | |/ /
257 257 # | o | | into | o / / # <--- fixed nodeline tail
258 258 # | |/ / | |/ /
259 259 # o | | o | |
260 260 fix_nodeline_tail = len(log_strings) <= 2 and not add_padding_line
261 261
262 262 # nodeline is the line containing the node character (@ or o).
263 263 nodeline = ["|", " "] * node_index
264 264 if node in repo_parents:
265 265 node_ch = "@"
266 266 else:
267 267 node_ch = "o"
268 268 nodeline.extend([node_ch, " "])
269 269
270 270 nodeline.extend(
271 271 get_nodeline_edges_tail(
272 272 node_index, prev_node_index, n_columns, n_columns_diff,
273 273 prev_n_columns_diff, fix_nodeline_tail))
274 274
275 275 # shift_interline is the line containing the non-vertical
276 276 # edges between this entry and the next.
277 277 shift_interline = ["|", " "] * node_index
278 278 if n_columns_diff == -1:
279 279 n_spaces = 1
280 280 edge_ch = "/"
281 281 elif n_columns_diff == 0:
282 282 n_spaces = 2
283 283 edge_ch = "|"
284 284 else:
285 285 n_spaces = 3
286 286 edge_ch = "\\"
287 287 shift_interline.extend(n_spaces * [" "])
288 288 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
289 289
290 290 # Draw edges from the current node to its parents.
291 291 draw_edges(edges, nodeline, shift_interline)
292 292
293 293 # lines is the list of all graph lines to print.
294 294 lines = [nodeline]
295 295 if add_padding_line:
296 296 lines.append(get_padding_line(node_index, n_columns, edges))
297 297 lines.append(shift_interline)
298 298
299 299 # Make sure that there are as many graph lines as there are
300 300 # log strings.
301 301 while len(log_strings) < len(lines):
302 302 log_strings.append("")
303 303 if len(lines) < len(log_strings):
304 304 extra_interline = ["|", " "] * (n_columns + n_columns_diff)
305 305 while len(lines) < len(log_strings):
306 306 lines.append(extra_interline)
307 307
308 308 # Print lines.
309 309 indentation_level = max(n_columns, n_columns + n_columns_diff)
310 310 for (line, logstr) in zip(lines, log_strings):
311 311 ui.write(format_line(line, indentation_level, logstr))
312 312
313 313 # ...and start over.
314 314 prev_node_index = node_index
315 315 prev_n_columns_diff = n_columns_diff
316 316
317 317 cmdtable = {
318 318 "glog":
319 319 (graphlog,
320 320 [('l', 'limit', '', _('limit number of changes displayed')),
321 321 ('p', 'patch', False, _('show patch')),
322 322 ('r', 'rev', [], _('show the specified revision or range')),
323 323 ('', 'style', '', _('display using template map file')),
324 324 ('', 'template', '', _('display with template'))],
325 _('hg glog [OPTION] [FILE]...')),
325 _('hg glog [OPTION]... [FILE]...')),
326 326 }
General Comments 0
You need to be logged in to leave comments. Login now