##// END OF EJS Templates
show: avoid extra list operations
Gregory Szorc -
r33206:9718725d default
parent child Browse files
Show More
@@ -1,432 +1,432 b''
1 # show.py - Extension implementing `hg show`
1 # show.py - Extension implementing `hg show`
2 #
2 #
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """unified command to show various repository information (EXPERIMENTAL)
8 """unified command to show various repository information (EXPERIMENTAL)
9
9
10 This extension provides the :hg:`show` command, which provides a central
10 This extension provides the :hg:`show` command, which provides a central
11 command for displaying commonly-accessed repository data and views of that
11 command for displaying commonly-accessed repository data and views of that
12 data.
12 data.
13
13
14 The following config options can influence operation.
14 The following config options can influence operation.
15
15
16 ``commands``
16 ``commands``
17 ------------
17 ------------
18
18
19 ``show.aliasprefix``
19 ``show.aliasprefix``
20 List of strings that will register aliases for views. e.g. ``s`` will
20 List of strings that will register aliases for views. e.g. ``s`` will
21 effectively set config options ``alias.s<view> = show <view>`` for all
21 effectively set config options ``alias.s<view> = show <view>`` for all
22 views. i.e. `hg swork` would execute `hg show work`.
22 views. i.e. `hg swork` would execute `hg show work`.
23
23
24 Aliases that would conflict with existing registrations will not be
24 Aliases that would conflict with existing registrations will not be
25 performed.
25 performed.
26 """
26 """
27
27
28 from __future__ import absolute_import
28 from __future__ import absolute_import
29
29
30 from mercurial.i18n import _
30 from mercurial.i18n import _
31 from mercurial.node import nullrev
31 from mercurial.node import nullrev
32 from mercurial import (
32 from mercurial import (
33 cmdutil,
33 cmdutil,
34 commands,
34 commands,
35 destutil,
35 destutil,
36 error,
36 error,
37 formatter,
37 formatter,
38 graphmod,
38 graphmod,
39 phases,
39 phases,
40 pycompat,
40 pycompat,
41 registrar,
41 registrar,
42 revset,
42 revset,
43 revsetlang,
43 revsetlang,
44 )
44 )
45
45
46 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
46 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
47 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
47 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
48 # be specifying the version(s) of Mercurial they are tested with, or
48 # be specifying the version(s) of Mercurial they are tested with, or
49 # leave the attribute unspecified.
49 # leave the attribute unspecified.
50 testedwith = 'ships-with-hg-core'
50 testedwith = 'ships-with-hg-core'
51
51
52 cmdtable = {}
52 cmdtable = {}
53 command = registrar.command(cmdtable)
53 command = registrar.command(cmdtable)
54 revsetpredicate = registrar.revsetpredicate()
54 revsetpredicate = registrar.revsetpredicate()
55
55
56 class showcmdfunc(registrar._funcregistrarbase):
56 class showcmdfunc(registrar._funcregistrarbase):
57 """Register a function to be invoked for an `hg show <thing>`."""
57 """Register a function to be invoked for an `hg show <thing>`."""
58
58
59 # Used by _formatdoc().
59 # Used by _formatdoc().
60 _docformat = '%s -- %s'
60 _docformat = '%s -- %s'
61
61
62 def _extrasetup(self, name, func, fmtopic=None, csettopic=None):
62 def _extrasetup(self, name, func, fmtopic=None, csettopic=None):
63 """Called with decorator arguments to register a show view.
63 """Called with decorator arguments to register a show view.
64
64
65 ``name`` is the sub-command name.
65 ``name`` is the sub-command name.
66
66
67 ``func`` is the function being decorated.
67 ``func`` is the function being decorated.
68
68
69 ``fmtopic`` is the topic in the style that will be rendered for
69 ``fmtopic`` is the topic in the style that will be rendered for
70 this view.
70 this view.
71
71
72 ``csettopic`` is the topic in the style to be used for a changeset
72 ``csettopic`` is the topic in the style to be used for a changeset
73 printer.
73 printer.
74
74
75 If ``fmtopic`` is specified, the view function will receive a
75 If ``fmtopic`` is specified, the view function will receive a
76 formatter instance. If ``csettopic`` is specified, the view
76 formatter instance. If ``csettopic`` is specified, the view
77 function will receive a changeset printer.
77 function will receive a changeset printer.
78 """
78 """
79 func._fmtopic = fmtopic
79 func._fmtopic = fmtopic
80 func._csettopic = csettopic
80 func._csettopic = csettopic
81
81
82 showview = showcmdfunc()
82 showview = showcmdfunc()
83
83
84 @command('show', [
84 @command('show', [
85 # TODO: Switch this template flag to use cmdutil.formatteropts if
85 # TODO: Switch this template flag to use cmdutil.formatteropts if
86 # 'hg show' becomes stable before --template/-T is stable. For now,
86 # 'hg show' becomes stable before --template/-T is stable. For now,
87 # we are putting it here without the '(EXPERIMENTAL)' flag because it
87 # we are putting it here without the '(EXPERIMENTAL)' flag because it
88 # is an important part of the 'hg show' user experience and the entire
88 # is an important part of the 'hg show' user experience and the entire
89 # 'hg show' experience is experimental.
89 # 'hg show' experience is experimental.
90 ('T', 'template', '', ('display with template'), _('TEMPLATE')),
90 ('T', 'template', '', ('display with template'), _('TEMPLATE')),
91 ], _('VIEW'))
91 ], _('VIEW'))
92 def show(ui, repo, view=None, template=None):
92 def show(ui, repo, view=None, template=None):
93 """show various repository information
93 """show various repository information
94
94
95 A requested view of repository data is displayed.
95 A requested view of repository data is displayed.
96
96
97 If no view is requested, the list of available views is shown and the
97 If no view is requested, the list of available views is shown and the
98 command aborts.
98 command aborts.
99
99
100 .. note::
100 .. note::
101
101
102 There are no backwards compatibility guarantees for the output of this
102 There are no backwards compatibility guarantees for the output of this
103 command. Output may change in any future Mercurial release.
103 command. Output may change in any future Mercurial release.
104
104
105 Consumers wanting stable command output should specify a template via
105 Consumers wanting stable command output should specify a template via
106 ``-T/--template``.
106 ``-T/--template``.
107
107
108 List of available views:
108 List of available views:
109 """
109 """
110 if ui.plain() and not template:
110 if ui.plain() and not template:
111 hint = _('invoke with -T/--template to control output format')
111 hint = _('invoke with -T/--template to control output format')
112 raise error.Abort(_('must specify a template in plain mode'), hint=hint)
112 raise error.Abort(_('must specify a template in plain mode'), hint=hint)
113
113
114 views = showview._table
114 views = showview._table
115
115
116 if not view:
116 if not view:
117 ui.pager('show')
117 ui.pager('show')
118 # TODO consider using formatter here so available views can be
118 # TODO consider using formatter here so available views can be
119 # rendered to custom format.
119 # rendered to custom format.
120 ui.write(_('available views:\n'))
120 ui.write(_('available views:\n'))
121 ui.write('\n')
121 ui.write('\n')
122
122
123 for name, func in sorted(views.items()):
123 for name, func in sorted(views.items()):
124 ui.write(('%s\n') % func.__doc__)
124 ui.write(('%s\n') % func.__doc__)
125
125
126 ui.write('\n')
126 ui.write('\n')
127 raise error.Abort(_('no view requested'),
127 raise error.Abort(_('no view requested'),
128 hint=_('use "hg show VIEW" to choose a view'))
128 hint=_('use "hg show VIEW" to choose a view'))
129
129
130 # TODO use same logic as dispatch to perform prefix matching.
130 # TODO use same logic as dispatch to perform prefix matching.
131 if view not in views:
131 if view not in views:
132 raise error.Abort(_('unknown view: %s') % view,
132 raise error.Abort(_('unknown view: %s') % view,
133 hint=_('run "hg show" to see available views'))
133 hint=_('run "hg show" to see available views'))
134
134
135 template = template or 'show'
135 template = template or 'show'
136
136
137 fn = views[view]
137 fn = views[view]
138 ui.pager('show')
138 ui.pager('show')
139
139
140 if fn._fmtopic:
140 if fn._fmtopic:
141 fmtopic = 'show%s' % fn._fmtopic
141 fmtopic = 'show%s' % fn._fmtopic
142 with ui.formatter(fmtopic, {'template': template}) as fm:
142 with ui.formatter(fmtopic, {'template': template}) as fm:
143 return fn(ui, repo, fm)
143 return fn(ui, repo, fm)
144 elif fn._csettopic:
144 elif fn._csettopic:
145 ref = 'show%s' % fn._csettopic
145 ref = 'show%s' % fn._csettopic
146 spec = formatter.lookuptemplate(ui, ref, template)
146 spec = formatter.lookuptemplate(ui, ref, template)
147 displayer = cmdutil.changeset_templater(ui, repo, spec, buffered=True)
147 displayer = cmdutil.changeset_templater(ui, repo, spec, buffered=True)
148 return fn(ui, repo, displayer)
148 return fn(ui, repo, displayer)
149 else:
149 else:
150 return fn(ui, repo)
150 return fn(ui, repo)
151
151
152 @showview('bookmarks', fmtopic='bookmarks')
152 @showview('bookmarks', fmtopic='bookmarks')
153 def showbookmarks(ui, repo, fm):
153 def showbookmarks(ui, repo, fm):
154 """bookmarks and their associated changeset"""
154 """bookmarks and their associated changeset"""
155 marks = repo._bookmarks
155 marks = repo._bookmarks
156 if not len(marks):
156 if not len(marks):
157 # This is a bit hacky. Ideally, templates would have a way to
157 # This is a bit hacky. Ideally, templates would have a way to
158 # specify an empty output, but we shouldn't corrupt JSON while
158 # specify an empty output, but we shouldn't corrupt JSON while
159 # waiting for this functionality.
159 # waiting for this functionality.
160 if not isinstance(fm, formatter.jsonformatter):
160 if not isinstance(fm, formatter.jsonformatter):
161 ui.write(_('(no bookmarks set)\n'))
161 ui.write(_('(no bookmarks set)\n'))
162 return
162 return
163
163
164 active = repo._activebookmark
164 active = repo._activebookmark
165 longestname = max(len(b) for b in marks)
165 longestname = max(len(b) for b in marks)
166 # TODO consider exposing longest shortest(node).
166 # TODO consider exposing longest shortest(node).
167
167
168 for bm, node in sorted(marks.items()):
168 for bm, node in sorted(marks.items()):
169 fm.startitem()
169 fm.startitem()
170 fm.context(ctx=repo[node])
170 fm.context(ctx=repo[node])
171 fm.write('bookmark', '%s', bm)
171 fm.write('bookmark', '%s', bm)
172 fm.write('node', fm.hexfunc(node), fm.hexfunc(node))
172 fm.write('node', fm.hexfunc(node), fm.hexfunc(node))
173 fm.data(active=bm == active,
173 fm.data(active=bm == active,
174 longestbookmarklen=longestname)
174 longestbookmarklen=longestname)
175
175
176 @showview('stack', csettopic='stack')
176 @showview('stack', csettopic='stack')
177 def showstack(ui, repo, displayer):
177 def showstack(ui, repo, displayer):
178 """current line of work"""
178 """current line of work"""
179 wdirctx = repo['.']
179 wdirctx = repo['.']
180 if wdirctx.rev() == nullrev:
180 if wdirctx.rev() == nullrev:
181 raise error.Abort(_('stack view only available when there is a '
181 raise error.Abort(_('stack view only available when there is a '
182 'working directory'))
182 'working directory'))
183
183
184 if wdirctx.phase() == phases.public:
184 if wdirctx.phase() == phases.public:
185 ui.write(_('(empty stack; working directory parent is a published '
185 ui.write(_('(empty stack; working directory parent is a published '
186 'changeset)\n'))
186 'changeset)\n'))
187 return
187 return
188
188
189 # TODO extract "find stack" into a function to facilitate
189 # TODO extract "find stack" into a function to facilitate
190 # customization and reuse.
190 # customization and reuse.
191
191
192 baserev = destutil.stackbase(ui, repo)
192 baserev = destutil.stackbase(ui, repo)
193 basectx = None
193 basectx = None
194
194
195 if baserev is None:
195 if baserev is None:
196 baserev = wdirctx.rev()
196 baserev = wdirctx.rev()
197 stackrevs = {wdirctx.rev()}
197 stackrevs = {wdirctx.rev()}
198 else:
198 else:
199 stackrevs = set(repo.revs('%d::.', baserev))
199 stackrevs = set(repo.revs('%d::.', baserev))
200
200
201 ctx = repo[baserev]
201 ctx = repo[baserev]
202 if ctx.p1().rev() != nullrev:
202 if ctx.p1().rev() != nullrev:
203 basectx = ctx.p1()
203 basectx = ctx.p1()
204
204
205 # And relevant descendants.
205 # And relevant descendants.
206 branchpointattip = False
206 branchpointattip = False
207 cl = repo.changelog
207 cl = repo.changelog
208
208
209 for rev in cl.descendants([wdirctx.rev()]):
209 for rev in cl.descendants([wdirctx.rev()]):
210 ctx = repo[rev]
210 ctx = repo[rev]
211
211
212 # Will only happen if . is public.
212 # Will only happen if . is public.
213 if ctx.phase() == phases.public:
213 if ctx.phase() == phases.public:
214 break
214 break
215
215
216 stackrevs.add(ctx.rev())
216 stackrevs.add(ctx.rev())
217
217
218 if len(ctx.children()) > 1:
218 if len(ctx.children()) > 1:
219 branchpointattip = True
219 branchpointattip = True
220 break
220 break
221
221
222 stackrevs = list(reversed(sorted(stackrevs)))
222 stackrevs = list(sorted(stackrevs, reverse=True))
223
223
224 # Find likely target heads for the current stack. These are likely
224 # Find likely target heads for the current stack. These are likely
225 # merge or rebase targets.
225 # merge or rebase targets.
226 if basectx:
226 if basectx:
227 # TODO make this customizable?
227 # TODO make this customizable?
228 newheads = set(repo.revs('heads(%d::) - %ld - not public()',
228 newheads = set(repo.revs('heads(%d::) - %ld - not public()',
229 basectx.rev(), stackrevs))
229 basectx.rev(), stackrevs))
230 else:
230 else:
231 newheads = set()
231 newheads = set()
232
232
233 try:
233 try:
234 cmdutil.findcmd('rebase', commands.table)
234 cmdutil.findcmd('rebase', commands.table)
235 haverebase = True
235 haverebase = True
236 except error.UnknownCommand:
236 except error.UnknownCommand:
237 haverebase = False
237 haverebase = False
238
238
239 # TODO use templating.
239 # TODO use templating.
240 # TODO consider using graphmod. But it may not be necessary given
240 # TODO consider using graphmod. But it may not be necessary given
241 # our simplicity and the customizations required.
241 # our simplicity and the customizations required.
242 # TODO use proper graph symbols from graphmod
242 # TODO use proper graph symbols from graphmod
243
243
244 shortesttmpl = formatter.maketemplater(ui, '{shortest(node, 5)}')
244 shortesttmpl = formatter.maketemplater(ui, '{shortest(node, 5)}')
245 def shortest(ctx):
245 def shortest(ctx):
246 return shortesttmpl.render({'ctx': ctx, 'node': ctx.hex()})
246 return shortesttmpl.render({'ctx': ctx, 'node': ctx.hex()})
247
247
248 # We write out new heads to aid in DAG awareness and to help with decision
248 # We write out new heads to aid in DAG awareness and to help with decision
249 # making on how the stack should be reconciled with commits made since the
249 # making on how the stack should be reconciled with commits made since the
250 # branch point.
250 # branch point.
251 if newheads:
251 if newheads:
252 # Calculate distance from base so we can render the count and so we can
252 # Calculate distance from base so we can render the count and so we can
253 # sort display order by commit distance.
253 # sort display order by commit distance.
254 revdistance = {}
254 revdistance = {}
255 for head in newheads:
255 for head in newheads:
256 # There is some redundancy in DAG traversal here and therefore
256 # There is some redundancy in DAG traversal here and therefore
257 # room to optimize.
257 # room to optimize.
258 ancestors = cl.ancestors([head], stoprev=basectx.rev())
258 ancestors = cl.ancestors([head], stoprev=basectx.rev())
259 revdistance[head] = len(list(ancestors))
259 revdistance[head] = len(list(ancestors))
260
260
261 sourcectx = repo[stackrevs[-1]]
261 sourcectx = repo[stackrevs[-1]]
262
262
263 sortedheads = sorted(newheads, key=lambda x: revdistance[x],
263 sortedheads = sorted(newheads, key=lambda x: revdistance[x],
264 reverse=True)
264 reverse=True)
265
265
266 for i, rev in enumerate(sortedheads):
266 for i, rev in enumerate(sortedheads):
267 ctx = repo[rev]
267 ctx = repo[rev]
268
268
269 if i:
269 if i:
270 ui.write(': ')
270 ui.write(': ')
271 else:
271 else:
272 ui.write(' ')
272 ui.write(' ')
273
273
274 ui.write(('o '))
274 ui.write(('o '))
275 displayer.show(ctx)
275 displayer.show(ctx)
276 displayer.flush(ctx)
276 displayer.flush(ctx)
277 ui.write('\n')
277 ui.write('\n')
278
278
279 if i:
279 if i:
280 ui.write(':/')
280 ui.write(':/')
281 else:
281 else:
282 ui.write(' /')
282 ui.write(' /')
283
283
284 ui.write(' (')
284 ui.write(' (')
285 ui.write(_('%d commits ahead') % revdistance[rev],
285 ui.write(_('%d commits ahead') % revdistance[rev],
286 label='stack.commitdistance')
286 label='stack.commitdistance')
287
287
288 if haverebase:
288 if haverebase:
289 # TODO may be able to omit --source in some scenarios
289 # TODO may be able to omit --source in some scenarios
290 ui.write('; ')
290 ui.write('; ')
291 ui.write(('hg rebase --source %s --dest %s' % (
291 ui.write(('hg rebase --source %s --dest %s' % (
292 shortest(sourcectx), shortest(ctx))),
292 shortest(sourcectx), shortest(ctx))),
293 label='stack.rebasehint')
293 label='stack.rebasehint')
294
294
295 ui.write(')\n')
295 ui.write(')\n')
296
296
297 ui.write(':\n: ')
297 ui.write(':\n: ')
298 ui.write(_('(stack head)\n'), label='stack.label')
298 ui.write(_('(stack head)\n'), label='stack.label')
299
299
300 if branchpointattip:
300 if branchpointattip:
301 ui.write(' \\ / ')
301 ui.write(' \\ / ')
302 ui.write(_('(multiple children)\n'), label='stack.label')
302 ui.write(_('(multiple children)\n'), label='stack.label')
303 ui.write(' |\n')
303 ui.write(' |\n')
304
304
305 for rev in stackrevs:
305 for rev in stackrevs:
306 ctx = repo[rev]
306 ctx = repo[rev]
307 symbol = '@' if rev == wdirctx.rev() else 'o'
307 symbol = '@' if rev == wdirctx.rev() else 'o'
308
308
309 if newheads:
309 if newheads:
310 ui.write(': ')
310 ui.write(': ')
311 else:
311 else:
312 ui.write(' ')
312 ui.write(' ')
313
313
314 ui.write(symbol, ' ')
314 ui.write(symbol, ' ')
315 displayer.show(ctx)
315 displayer.show(ctx)
316 displayer.flush(ctx)
316 displayer.flush(ctx)
317 ui.write('\n')
317 ui.write('\n')
318
318
319 # TODO display histedit hint?
319 # TODO display histedit hint?
320
320
321 if basectx:
321 if basectx:
322 # Vertically and horizontally separate stack base from parent
322 # Vertically and horizontally separate stack base from parent
323 # to reinforce stack boundary.
323 # to reinforce stack boundary.
324 if newheads:
324 if newheads:
325 ui.write(':/ ')
325 ui.write(':/ ')
326 else:
326 else:
327 ui.write(' / ')
327 ui.write(' / ')
328
328
329 ui.write(_('(stack base)'), '\n', label='stack.label')
329 ui.write(_('(stack base)'), '\n', label='stack.label')
330 ui.write(('o '))
330 ui.write(('o '))
331
331
332 displayer.show(basectx)
332 displayer.show(basectx)
333 displayer.flush(basectx)
333 displayer.flush(basectx)
334 ui.write('\n')
334 ui.write('\n')
335
335
336 @revsetpredicate('_underway([commitage[, headage]])')
336 @revsetpredicate('_underway([commitage[, headage]])')
337 def underwayrevset(repo, subset, x):
337 def underwayrevset(repo, subset, x):
338 args = revset.getargsdict(x, 'underway', 'commitage headage')
338 args = revset.getargsdict(x, 'underway', 'commitage headage')
339 if 'commitage' not in args:
339 if 'commitage' not in args:
340 args['commitage'] = None
340 args['commitage'] = None
341 if 'headage' not in args:
341 if 'headage' not in args:
342 args['headage'] = None
342 args['headage'] = None
343
343
344 # We assume callers of this revset add a topographical sort on the
344 # We assume callers of this revset add a topographical sort on the
345 # result. This means there is no benefit to making the revset lazy
345 # result. This means there is no benefit to making the revset lazy
346 # since the topographical sort needs to consume all revs.
346 # since the topographical sort needs to consume all revs.
347 #
347 #
348 # With this in mind, we build up the set manually instead of constructing
348 # With this in mind, we build up the set manually instead of constructing
349 # a complex revset. This enables faster execution.
349 # a complex revset. This enables faster execution.
350
350
351 # Mutable changesets (non-public) are the most important changesets
351 # Mutable changesets (non-public) are the most important changesets
352 # to return. ``not public()`` will also pull in obsolete changesets if
352 # to return. ``not public()`` will also pull in obsolete changesets if
353 # there is a non-obsolete changeset with obsolete ancestors. This is
353 # there is a non-obsolete changeset with obsolete ancestors. This is
354 # why we exclude obsolete changesets from this query.
354 # why we exclude obsolete changesets from this query.
355 rs = 'not public() and not obsolete()'
355 rs = 'not public() and not obsolete()'
356 rsargs = []
356 rsargs = []
357 if args['commitage']:
357 if args['commitage']:
358 rs += ' and date(%s)'
358 rs += ' and date(%s)'
359 rsargs.append(revsetlang.getstring(args['commitage'],
359 rsargs.append(revsetlang.getstring(args['commitage'],
360 _('commitage requires a string')))
360 _('commitage requires a string')))
361
361
362 mutable = repo.revs(rs, *rsargs)
362 mutable = repo.revs(rs, *rsargs)
363 relevant = revset.baseset(mutable)
363 relevant = revset.baseset(mutable)
364
364
365 # Add parents of mutable changesets to provide context.
365 # Add parents of mutable changesets to provide context.
366 relevant += repo.revs('parents(%ld)', mutable)
366 relevant += repo.revs('parents(%ld)', mutable)
367
367
368 # We also pull in (public) heads if they a) aren't closing a branch
368 # We also pull in (public) heads if they a) aren't closing a branch
369 # b) are recent.
369 # b) are recent.
370 rs = 'head() and not closed()'
370 rs = 'head() and not closed()'
371 rsargs = []
371 rsargs = []
372 if args['headage']:
372 if args['headage']:
373 rs += ' and date(%s)'
373 rs += ' and date(%s)'
374 rsargs.append(revsetlang.getstring(args['headage'],
374 rsargs.append(revsetlang.getstring(args['headage'],
375 _('headage requires a string')))
375 _('headage requires a string')))
376
376
377 relevant += repo.revs(rs, *rsargs)
377 relevant += repo.revs(rs, *rsargs)
378
378
379 # Add working directory parent.
379 # Add working directory parent.
380 wdirrev = repo['.'].rev()
380 wdirrev = repo['.'].rev()
381 if wdirrev != nullrev:
381 if wdirrev != nullrev:
382 relevant += revset.baseset({wdirrev})
382 relevant += revset.baseset({wdirrev})
383
383
384 return subset & relevant
384 return subset & relevant
385
385
386 @showview('work', csettopic='work')
386 @showview('work', csettopic='work')
387 def showwork(ui, repo, displayer):
387 def showwork(ui, repo, displayer):
388 """changesets that aren't finished"""
388 """changesets that aren't finished"""
389 # TODO support date-based limiting when calling revset.
389 # TODO support date-based limiting when calling revset.
390 revs = repo.revs('sort(_underway(), topo)')
390 revs = repo.revs('sort(_underway(), topo)')
391
391
392 revdag = graphmod.dagwalker(repo, revs)
392 revdag = graphmod.dagwalker(repo, revs)
393
393
394 ui.setconfig('experimental', 'graphshorten', True)
394 ui.setconfig('experimental', 'graphshorten', True)
395 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
395 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
396
396
397 def extsetup(ui):
397 def extsetup(ui):
398 # Alias `hg <prefix><view>` to `hg show <view>`.
398 # Alias `hg <prefix><view>` to `hg show <view>`.
399 for prefix in ui.configlist('commands', 'show.aliasprefix'):
399 for prefix in ui.configlist('commands', 'show.aliasprefix'):
400 for view in showview._table:
400 for view in showview._table:
401 name = '%s%s' % (prefix, view)
401 name = '%s%s' % (prefix, view)
402
402
403 choice, allcommands = cmdutil.findpossible(name, commands.table,
403 choice, allcommands = cmdutil.findpossible(name, commands.table,
404 strict=True)
404 strict=True)
405
405
406 # This alias is already a command name. Don't set it.
406 # This alias is already a command name. Don't set it.
407 if name in choice:
407 if name in choice:
408 continue
408 continue
409
409
410 # Same for aliases.
410 # Same for aliases.
411 if ui.config('alias', name):
411 if ui.config('alias', name):
412 continue
412 continue
413
413
414 ui.setconfig('alias', name, 'show %s' % view, source='show')
414 ui.setconfig('alias', name, 'show %s' % view, source='show')
415
415
416 # Adjust the docstring of the show command so it shows all registered views.
416 # Adjust the docstring of the show command so it shows all registered views.
417 # This is a bit hacky because it runs at the end of module load. When moved
417 # This is a bit hacky because it runs at the end of module load. When moved
418 # into core or when another extension wants to provide a view, we'll need
418 # into core or when another extension wants to provide a view, we'll need
419 # to do this more robustly.
419 # to do this more robustly.
420 # TODO make this more robust.
420 # TODO make this more robust.
421 def _updatedocstring():
421 def _updatedocstring():
422 longest = max(map(len, showview._table.keys()))
422 longest = max(map(len, showview._table.keys()))
423 entries = []
423 entries = []
424 for key in sorted(showview._table.keys()):
424 for key in sorted(showview._table.keys()):
425 entries.append(pycompat.sysstr(' %s %s' % (
425 entries.append(pycompat.sysstr(' %s %s' % (
426 key.ljust(longest), showview._table[key]._origdoc)))
426 key.ljust(longest), showview._table[key]._origdoc)))
427
427
428 cmdtable['show'][0].__doc__ = pycompat.sysstr('%s\n\n%s\n ') % (
428 cmdtable['show'][0].__doc__ = pycompat.sysstr('%s\n\n%s\n ') % (
429 cmdtable['show'][0].__doc__.rstrip(),
429 cmdtable['show'][0].__doc__.rstrip(),
430 pycompat.sysstr('\n\n').join(entries))
430 pycompat.sysstr('\n\n').join(entries))
431
431
432 _updatedocstring()
432 _updatedocstring()
General Comments 0
You need to be logged in to leave comments. Login now