##// END OF EJS Templates
graphlog: remove unused arg from _wrapcmd
Idan Kamara -
r14416:253bda94 default
parent child Browse files
Show More
@@ -1,397 +1,397 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 the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to view revision graphs from a shell
9 9
10 10 This extension adds a --graph option to the incoming, outgoing and log
11 11 commands. When this options is given, an ASCII representation of the
12 12 revision graph is also shown.
13 13 '''
14 14
15 15 from mercurial.cmdutil import show_changeset
16 16 from mercurial.commands import templateopts
17 17 from mercurial.i18n import _
18 18 from mercurial.node import nullrev
19 19 from mercurial import cmdutil, commands, extensions, scmutil
20 20 from mercurial import hg, util, graphmod
21 21
22 22 cmdtable = {}
23 23 command = cmdutil.command(cmdtable)
24 24
25 25 ASCIIDATA = 'ASC'
26 26
27 27 def asciiedges(type, char, lines, seen, rev, parents):
28 28 """adds edge info to changelog DAG walk suitable for ascii()"""
29 29 if rev not in seen:
30 30 seen.append(rev)
31 31 nodeidx = seen.index(rev)
32 32
33 33 knownparents = []
34 34 newparents = []
35 35 for parent in parents:
36 36 if parent in seen:
37 37 knownparents.append(parent)
38 38 else:
39 39 newparents.append(parent)
40 40
41 41 ncols = len(seen)
42 42 nextseen = seen[:]
43 43 nextseen[nodeidx:nodeidx + 1] = newparents
44 44 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
45 45
46 46 while len(newparents) > 2:
47 47 # ascii() only knows how to add or remove a single column between two
48 48 # calls. Nodes with more than two parents break this constraint so we
49 49 # introduce intermediate expansion lines to grow the active node list
50 50 # slowly.
51 51 edges.append((nodeidx, nodeidx))
52 52 edges.append((nodeidx, nodeidx + 1))
53 53 nmorecols = 1
54 54 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
55 55 char = '\\'
56 56 lines = []
57 57 nodeidx += 1
58 58 ncols += 1
59 59 edges = []
60 60 del newparents[0]
61 61
62 62 if len(newparents) > 0:
63 63 edges.append((nodeidx, nodeidx))
64 64 if len(newparents) > 1:
65 65 edges.append((nodeidx, nodeidx + 1))
66 66 nmorecols = len(nextseen) - ncols
67 67 seen[:] = nextseen
68 68 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
69 69
70 70 def fix_long_right_edges(edges):
71 71 for (i, (start, end)) in enumerate(edges):
72 72 if end > start:
73 73 edges[i] = (start, end + 1)
74 74
75 75 def get_nodeline_edges_tail(
76 76 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
77 77 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
78 78 # Still going in the same non-vertical direction.
79 79 if n_columns_diff == -1:
80 80 start = max(node_index + 1, p_node_index)
81 81 tail = ["|", " "] * (start - node_index - 1)
82 82 tail.extend(["/", " "] * (n_columns - start))
83 83 return tail
84 84 else:
85 85 return ["\\", " "] * (n_columns - node_index - 1)
86 86 else:
87 87 return ["|", " "] * (n_columns - node_index - 1)
88 88
89 89 def draw_edges(edges, nodeline, interline):
90 90 for (start, end) in edges:
91 91 if start == end + 1:
92 92 interline[2 * end + 1] = "/"
93 93 elif start == end - 1:
94 94 interline[2 * start + 1] = "\\"
95 95 elif start == end:
96 96 interline[2 * start] = "|"
97 97 else:
98 98 nodeline[2 * end] = "+"
99 99 if start > end:
100 100 (start, end) = (end, start)
101 101 for i in range(2 * start + 1, 2 * end):
102 102 if nodeline[i] != "+":
103 103 nodeline[i] = "-"
104 104
105 105 def get_padding_line(ni, n_columns, edges):
106 106 line = []
107 107 line.extend(["|", " "] * ni)
108 108 if (ni, ni - 1) in edges or (ni, ni) in edges:
109 109 # (ni, ni - 1) (ni, ni)
110 110 # | | | | | | | |
111 111 # +---o | | o---+
112 112 # | | c | | c | |
113 113 # | |/ / | |/ /
114 114 # | | | | | |
115 115 c = "|"
116 116 else:
117 117 c = " "
118 118 line.extend([c, " "])
119 119 line.extend(["|", " "] * (n_columns - ni - 1))
120 120 return line
121 121
122 122 def asciistate():
123 123 """returns the initial value for the "state" argument to ascii()"""
124 124 return [0, 0]
125 125
126 126 def ascii(ui, state, type, char, text, coldata):
127 127 """prints an ASCII graph of the DAG
128 128
129 129 takes the following arguments (one call per node in the graph):
130 130
131 131 - ui to write to
132 132 - Somewhere to keep the needed state in (init to asciistate())
133 133 - Column of the current node in the set of ongoing edges.
134 134 - Type indicator of node data == ASCIIDATA.
135 135 - Payload: (char, lines):
136 136 - Character to use as node's symbol.
137 137 - List of lines to display as the node's text.
138 138 - Edges; a list of (col, next_col) indicating the edges between
139 139 the current node and its parents.
140 140 - Number of columns (ongoing edges) in the current revision.
141 141 - The difference between the number of columns (ongoing edges)
142 142 in the next revision and the number of columns (ongoing edges)
143 143 in the current revision. That is: -1 means one column removed;
144 144 0 means no columns added or removed; 1 means one column added.
145 145 """
146 146
147 147 idx, edges, ncols, coldiff = coldata
148 148 assert -2 < coldiff < 2
149 149 if coldiff == -1:
150 150 # Transform
151 151 #
152 152 # | | | | | |
153 153 # o | | into o---+
154 154 # |X / |/ /
155 155 # | | | |
156 156 fix_long_right_edges(edges)
157 157
158 158 # add_padding_line says whether to rewrite
159 159 #
160 160 # | | | | | | | |
161 161 # | o---+ into | o---+
162 162 # | / / | | | # <--- padding line
163 163 # o | | | / /
164 164 # o | |
165 165 add_padding_line = (len(text) > 2 and coldiff == -1 and
166 166 [x for (x, y) in edges if x + 1 < y])
167 167
168 168 # fix_nodeline_tail says whether to rewrite
169 169 #
170 170 # | | o | | | | o | |
171 171 # | | |/ / | | |/ /
172 172 # | o | | into | o / / # <--- fixed nodeline tail
173 173 # | |/ / | |/ /
174 174 # o | | o | |
175 175 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
176 176
177 177 # nodeline is the line containing the node character (typically o)
178 178 nodeline = ["|", " "] * idx
179 179 nodeline.extend([char, " "])
180 180
181 181 nodeline.extend(
182 182 get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
183 183 state[0], fix_nodeline_tail))
184 184
185 185 # shift_interline is the line containing the non-vertical
186 186 # edges between this entry and the next
187 187 shift_interline = ["|", " "] * idx
188 188 if coldiff == -1:
189 189 n_spaces = 1
190 190 edge_ch = "/"
191 191 elif coldiff == 0:
192 192 n_spaces = 2
193 193 edge_ch = "|"
194 194 else:
195 195 n_spaces = 3
196 196 edge_ch = "\\"
197 197 shift_interline.extend(n_spaces * [" "])
198 198 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
199 199
200 200 # draw edges from the current node to its parents
201 201 draw_edges(edges, nodeline, shift_interline)
202 202
203 203 # lines is the list of all graph lines to print
204 204 lines = [nodeline]
205 205 if add_padding_line:
206 206 lines.append(get_padding_line(idx, ncols, edges))
207 207 lines.append(shift_interline)
208 208
209 209 # make sure that there are as many graph lines as there are
210 210 # log strings
211 211 while len(text) < len(lines):
212 212 text.append("")
213 213 if len(lines) < len(text):
214 214 extra_interline = ["|", " "] * (ncols + coldiff)
215 215 while len(lines) < len(text):
216 216 lines.append(extra_interline)
217 217
218 218 # print lines
219 219 indentation_level = max(ncols, ncols + coldiff)
220 220 for (line, logstr) in zip(lines, text):
221 221 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
222 222 ui.write(ln.rstrip() + '\n')
223 223
224 224 # ... and start over
225 225 state[0] = coldiff
226 226 state[1] = idx
227 227
228 228 def get_revs(repo, rev_opt):
229 229 if rev_opt:
230 230 revs = scmutil.revrange(repo, rev_opt)
231 231 if len(revs) == 0:
232 232 return (nullrev, nullrev)
233 233 return (max(revs), min(revs))
234 234 else:
235 235 return (len(repo) - 1, 0)
236 236
237 237 def check_unsupported_flags(pats, opts):
238 238 for op in ["follow_first", "copies", "newest_first"]:
239 239 if op in opts and opts[op]:
240 240 raise util.Abort(_("-G/--graph option is incompatible with --%s")
241 241 % op.replace("_", "-"))
242 242 if pats and opts.get('follow'):
243 243 raise util.Abort(_("-G/--graph option is incompatible with --follow "
244 244 "with file argument"))
245 245
246 246 def revset(pats, opts):
247 247 """Return revset str built of revisions, log options and file patterns.
248 248 """
249 249 opt2revset = {
250 250 'follow': (0, 'follow()'),
251 251 'no_merges': (0, 'not merge()'),
252 252 'only_merges': (0, 'merge()'),
253 253 'removed': (0, 'removes("*")'),
254 254 'date': (1, 'date($)'),
255 255 'branch': (2, 'branch($)'),
256 256 'exclude': (2, 'not file($)'),
257 257 'include': (2, 'file($)'),
258 258 'keyword': (2, 'keyword($)'),
259 259 'only_branch': (2, 'branch($)'),
260 260 'prune': (2, 'not ($ or ancestors($))'),
261 261 'user': (2, 'user($)'),
262 262 }
263 263 optrevset = []
264 264 revset = []
265 265 for op, val in opts.iteritems():
266 266 if not val:
267 267 continue
268 268 if op == 'rev':
269 269 # Already a revset
270 270 revset.extend(val)
271 271 if op not in opt2revset:
272 272 continue
273 273 arity, revop = opt2revset[op]
274 274 revop = revop.replace('$', '%(val)r')
275 275 if arity == 0:
276 276 optrevset.append(revop)
277 277 elif arity == 1:
278 278 optrevset.append(revop % {'val': val})
279 279 else:
280 280 for f in val:
281 281 optrevset.append(revop % {'val': f})
282 282
283 283 for path in pats:
284 284 optrevset.append('file(%r)' % path)
285 285
286 286 if revset or optrevset:
287 287 if revset:
288 288 revset = ['(' + ' or '.join(revset) + ')']
289 289 if optrevset:
290 290 revset.append('(' + ' and '.join(optrevset) + ')')
291 291 revset = ' and '.join(revset)
292 292 else:
293 293 revset = 'all()'
294 294 return revset
295 295
296 296 def generate(ui, dag, displayer, showparents, edgefn):
297 297 seen, state = [], asciistate()
298 298 for rev, type, ctx, parents in dag:
299 299 char = ctx.node() in showparents and '@' or 'o'
300 300 displayer.show(ctx)
301 301 lines = displayer.hunk.pop(rev).split('\n')[:-1]
302 302 displayer.flush(rev)
303 303 edges = edgefn(type, char, lines, seen, rev, parents)
304 304 for type, char, lines, coldata in edges:
305 305 ascii(ui, state, type, char, lines, coldata)
306 306 displayer.close()
307 307
308 308 @command('glog',
309 309 [('l', 'limit', '',
310 310 _('limit number of changes displayed'), _('NUM')),
311 311 ('p', 'patch', False, _('show patch')),
312 312 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
313 313 ] + templateopts,
314 314 _('hg glog [OPTION]... [FILE]'))
315 315 def graphlog(ui, repo, *pats, **opts):
316 316 """show revision history alongside an ASCII revision graph
317 317
318 318 Print a revision history alongside a revision graph drawn with
319 319 ASCII characters.
320 320
321 321 Nodes printed as an @ character are parents of the working
322 322 directory.
323 323 """
324 324
325 325 check_unsupported_flags(pats, opts)
326 326
327 327 revs = sorted(scmutil.revrange(repo, [revset(pats, opts)]), reverse=1)
328 328 limit = cmdutil.loglimit(opts)
329 329 if limit is not None:
330 330 revs = revs[:limit]
331 331 revdag = graphmod.dagwalker(repo, revs)
332 332
333 333 displayer = show_changeset(ui, repo, opts, buffered=True)
334 334 showparents = [ctx.node() for ctx in repo[None].parents()]
335 335 generate(ui, revdag, displayer, showparents, asciiedges)
336 336
337 337 def graphrevs(repo, nodes, opts):
338 338 limit = cmdutil.loglimit(opts)
339 339 nodes.reverse()
340 340 if limit is not None:
341 341 nodes = nodes[:limit]
342 342 return graphmod.nodes(repo, nodes)
343 343
344 344 def goutgoing(ui, repo, dest=None, **opts):
345 345 """show the outgoing changesets alongside an ASCII revision graph
346 346
347 347 Print the outgoing changesets alongside a revision graph drawn with
348 348 ASCII characters.
349 349
350 350 Nodes printed as an @ character are parents of the working
351 351 directory.
352 352 """
353 353
354 354 check_unsupported_flags([], opts)
355 355 o = hg._outgoing(ui, repo, dest, opts)
356 356 if o is None:
357 357 return
358 358
359 359 revdag = graphrevs(repo, o, opts)
360 360 displayer = show_changeset(ui, repo, opts, buffered=True)
361 361 showparents = [ctx.node() for ctx in repo[None].parents()]
362 362 generate(ui, revdag, displayer, showparents, asciiedges)
363 363
364 364 def gincoming(ui, repo, source="default", **opts):
365 365 """show the incoming changesets alongside an ASCII revision graph
366 366
367 367 Print the incoming changesets alongside a revision graph drawn with
368 368 ASCII characters.
369 369
370 370 Nodes printed as an @ character are parents of the working
371 371 directory.
372 372 """
373 373 def subreporecurse():
374 374 return 1
375 375
376 376 check_unsupported_flags([], opts)
377 377 def display(other, chlist, displayer):
378 378 revdag = graphrevs(other, chlist, opts)
379 379 showparents = [ctx.node() for ctx in repo[None].parents()]
380 380 generate(ui, revdag, displayer, showparents, asciiedges)
381 381
382 382 hg._incoming(display, subreporecurse, ui, repo, source, opts, buffered=True)
383 383
384 384 def uisetup(ui):
385 385 '''Initialize the extension.'''
386 _wrapcmd(ui, 'log', commands.table, graphlog)
387 _wrapcmd(ui, 'incoming', commands.table, gincoming)
388 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
386 _wrapcmd('log', commands.table, graphlog)
387 _wrapcmd('incoming', commands.table, gincoming)
388 _wrapcmd('outgoing', commands.table, goutgoing)
389 389
390 def _wrapcmd(ui, cmd, table, wrapfn):
390 def _wrapcmd(cmd, table, wrapfn):
391 391 '''wrap the command'''
392 392 def graph(orig, *args, **kwargs):
393 393 if kwargs['graph']:
394 394 return wrapfn(*args, **kwargs)
395 395 return orig(*args, **kwargs)
396 396 entry = extensions.wrapcommand(table, cmd, graph)
397 397 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
General Comments 0
You need to be logged in to leave comments. Login now