##// END OF EJS Templates
show: document why accidentally quadratic is (probably) acceptable
Gregory Szorc -
r33208:f48422ab default
parent child Browse files
Show More
@@ -1,432 +1,437 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 # ctx.children() within a function iterating on descandants
219 # potentially has severe performance concerns because revlog.children()
220 # iterates over all revisions after ctx's node. However, the number of
221 # draft changesets should be a reasonably small number. So even if
222 # this is quadratic, the perf impact should be minimal.
218 if len(ctx.children()) > 1:
223 if len(ctx.children()) > 1:
219 branchpointattip = True
224 branchpointattip = True
220 break
225 break
221
226
222 stackrevs = list(sorted(stackrevs, reverse=True))
227 stackrevs = list(sorted(stackrevs, reverse=True))
223
228
224 # Find likely target heads for the current stack. These are likely
229 # Find likely target heads for the current stack. These are likely
225 # merge or rebase targets.
230 # merge or rebase targets.
226 if basectx:
231 if basectx:
227 # TODO make this customizable?
232 # TODO make this customizable?
228 newheads = set(repo.revs('heads(%d::) - %ld - not public()',
233 newheads = set(repo.revs('heads(%d::) - %ld - not public()',
229 basectx.rev(), stackrevs))
234 basectx.rev(), stackrevs))
230 else:
235 else:
231 newheads = set()
236 newheads = set()
232
237
233 try:
238 try:
234 cmdutil.findcmd('rebase', commands.table)
239 cmdutil.findcmd('rebase', commands.table)
235 haverebase = True
240 haverebase = True
236 except (error.AmbiguousCommand, error.UnknownCommand):
241 except (error.AmbiguousCommand, error.UnknownCommand):
237 haverebase = False
242 haverebase = False
238
243
239 # TODO use templating.
244 # TODO use templating.
240 # TODO consider using graphmod. But it may not be necessary given
245 # TODO consider using graphmod. But it may not be necessary given
241 # our simplicity and the customizations required.
246 # our simplicity and the customizations required.
242 # TODO use proper graph symbols from graphmod
247 # TODO use proper graph symbols from graphmod
243
248
244 shortesttmpl = formatter.maketemplater(ui, '{shortest(node, 5)}')
249 shortesttmpl = formatter.maketemplater(ui, '{shortest(node, 5)}')
245 def shortest(ctx):
250 def shortest(ctx):
246 return shortesttmpl.render({'ctx': ctx, 'node': ctx.hex()})
251 return shortesttmpl.render({'ctx': ctx, 'node': ctx.hex()})
247
252
248 # We write out new heads to aid in DAG awareness and to help with decision
253 # 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
254 # making on how the stack should be reconciled with commits made since the
250 # branch point.
255 # branch point.
251 if newheads:
256 if newheads:
252 # Calculate distance from base so we can render the count and so we can
257 # Calculate distance from base so we can render the count and so we can
253 # sort display order by commit distance.
258 # sort display order by commit distance.
254 revdistance = {}
259 revdistance = {}
255 for head in newheads:
260 for head in newheads:
256 # There is some redundancy in DAG traversal here and therefore
261 # There is some redundancy in DAG traversal here and therefore
257 # room to optimize.
262 # room to optimize.
258 ancestors = cl.ancestors([head], stoprev=basectx.rev())
263 ancestors = cl.ancestors([head], stoprev=basectx.rev())
259 revdistance[head] = len(list(ancestors))
264 revdistance[head] = len(list(ancestors))
260
265
261 sourcectx = repo[stackrevs[-1]]
266 sourcectx = repo[stackrevs[-1]]
262
267
263 sortedheads = sorted(newheads, key=lambda x: revdistance[x],
268 sortedheads = sorted(newheads, key=lambda x: revdistance[x],
264 reverse=True)
269 reverse=True)
265
270
266 for i, rev in enumerate(sortedheads):
271 for i, rev in enumerate(sortedheads):
267 ctx = repo[rev]
272 ctx = repo[rev]
268
273
269 if i:
274 if i:
270 ui.write(': ')
275 ui.write(': ')
271 else:
276 else:
272 ui.write(' ')
277 ui.write(' ')
273
278
274 ui.write(('o '))
279 ui.write(('o '))
275 displayer.show(ctx)
280 displayer.show(ctx)
276 displayer.flush(ctx)
281 displayer.flush(ctx)
277 ui.write('\n')
282 ui.write('\n')
278
283
279 if i:
284 if i:
280 ui.write(':/')
285 ui.write(':/')
281 else:
286 else:
282 ui.write(' /')
287 ui.write(' /')
283
288
284 ui.write(' (')
289 ui.write(' (')
285 ui.write(_('%d commits ahead') % revdistance[rev],
290 ui.write(_('%d commits ahead') % revdistance[rev],
286 label='stack.commitdistance')
291 label='stack.commitdistance')
287
292
288 if haverebase:
293 if haverebase:
289 # TODO may be able to omit --source in some scenarios
294 # TODO may be able to omit --source in some scenarios
290 ui.write('; ')
295 ui.write('; ')
291 ui.write(('hg rebase --source %s --dest %s' % (
296 ui.write(('hg rebase --source %s --dest %s' % (
292 shortest(sourcectx), shortest(ctx))),
297 shortest(sourcectx), shortest(ctx))),
293 label='stack.rebasehint')
298 label='stack.rebasehint')
294
299
295 ui.write(')\n')
300 ui.write(')\n')
296
301
297 ui.write(':\n: ')
302 ui.write(':\n: ')
298 ui.write(_('(stack head)\n'), label='stack.label')
303 ui.write(_('(stack head)\n'), label='stack.label')
299
304
300 if branchpointattip:
305 if branchpointattip:
301 ui.write(' \\ / ')
306 ui.write(' \\ / ')
302 ui.write(_('(multiple children)\n'), label='stack.label')
307 ui.write(_('(multiple children)\n'), label='stack.label')
303 ui.write(' |\n')
308 ui.write(' |\n')
304
309
305 for rev in stackrevs:
310 for rev in stackrevs:
306 ctx = repo[rev]
311 ctx = repo[rev]
307 symbol = '@' if rev == wdirctx.rev() else 'o'
312 symbol = '@' if rev == wdirctx.rev() else 'o'
308
313
309 if newheads:
314 if newheads:
310 ui.write(': ')
315 ui.write(': ')
311 else:
316 else:
312 ui.write(' ')
317 ui.write(' ')
313
318
314 ui.write(symbol, ' ')
319 ui.write(symbol, ' ')
315 displayer.show(ctx)
320 displayer.show(ctx)
316 displayer.flush(ctx)
321 displayer.flush(ctx)
317 ui.write('\n')
322 ui.write('\n')
318
323
319 # TODO display histedit hint?
324 # TODO display histedit hint?
320
325
321 if basectx:
326 if basectx:
322 # Vertically and horizontally separate stack base from parent
327 # Vertically and horizontally separate stack base from parent
323 # to reinforce stack boundary.
328 # to reinforce stack boundary.
324 if newheads:
329 if newheads:
325 ui.write(':/ ')
330 ui.write(':/ ')
326 else:
331 else:
327 ui.write(' / ')
332 ui.write(' / ')
328
333
329 ui.write(_('(stack base)'), '\n', label='stack.label')
334 ui.write(_('(stack base)'), '\n', label='stack.label')
330 ui.write(('o '))
335 ui.write(('o '))
331
336
332 displayer.show(basectx)
337 displayer.show(basectx)
333 displayer.flush(basectx)
338 displayer.flush(basectx)
334 ui.write('\n')
339 ui.write('\n')
335
340
336 @revsetpredicate('_underway([commitage[, headage]])')
341 @revsetpredicate('_underway([commitage[, headage]])')
337 def underwayrevset(repo, subset, x):
342 def underwayrevset(repo, subset, x):
338 args = revset.getargsdict(x, 'underway', 'commitage headage')
343 args = revset.getargsdict(x, 'underway', 'commitage headage')
339 if 'commitage' not in args:
344 if 'commitage' not in args:
340 args['commitage'] = None
345 args['commitage'] = None
341 if 'headage' not in args:
346 if 'headage' not in args:
342 args['headage'] = None
347 args['headage'] = None
343
348
344 # We assume callers of this revset add a topographical sort on the
349 # 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
350 # result. This means there is no benefit to making the revset lazy
346 # since the topographical sort needs to consume all revs.
351 # since the topographical sort needs to consume all revs.
347 #
352 #
348 # With this in mind, we build up the set manually instead of constructing
353 # With this in mind, we build up the set manually instead of constructing
349 # a complex revset. This enables faster execution.
354 # a complex revset. This enables faster execution.
350
355
351 # Mutable changesets (non-public) are the most important changesets
356 # Mutable changesets (non-public) are the most important changesets
352 # to return. ``not public()`` will also pull in obsolete changesets if
357 # to return. ``not public()`` will also pull in obsolete changesets if
353 # there is a non-obsolete changeset with obsolete ancestors. This is
358 # there is a non-obsolete changeset with obsolete ancestors. This is
354 # why we exclude obsolete changesets from this query.
359 # why we exclude obsolete changesets from this query.
355 rs = 'not public() and not obsolete()'
360 rs = 'not public() and not obsolete()'
356 rsargs = []
361 rsargs = []
357 if args['commitage']:
362 if args['commitage']:
358 rs += ' and date(%s)'
363 rs += ' and date(%s)'
359 rsargs.append(revsetlang.getstring(args['commitage'],
364 rsargs.append(revsetlang.getstring(args['commitage'],
360 _('commitage requires a string')))
365 _('commitage requires a string')))
361
366
362 mutable = repo.revs(rs, *rsargs)
367 mutable = repo.revs(rs, *rsargs)
363 relevant = revset.baseset(mutable)
368 relevant = revset.baseset(mutable)
364
369
365 # Add parents of mutable changesets to provide context.
370 # Add parents of mutable changesets to provide context.
366 relevant += repo.revs('parents(%ld)', mutable)
371 relevant += repo.revs('parents(%ld)', mutable)
367
372
368 # We also pull in (public) heads if they a) aren't closing a branch
373 # We also pull in (public) heads if they a) aren't closing a branch
369 # b) are recent.
374 # b) are recent.
370 rs = 'head() and not closed()'
375 rs = 'head() and not closed()'
371 rsargs = []
376 rsargs = []
372 if args['headage']:
377 if args['headage']:
373 rs += ' and date(%s)'
378 rs += ' and date(%s)'
374 rsargs.append(revsetlang.getstring(args['headage'],
379 rsargs.append(revsetlang.getstring(args['headage'],
375 _('headage requires a string')))
380 _('headage requires a string')))
376
381
377 relevant += repo.revs(rs, *rsargs)
382 relevant += repo.revs(rs, *rsargs)
378
383
379 # Add working directory parent.
384 # Add working directory parent.
380 wdirrev = repo['.'].rev()
385 wdirrev = repo['.'].rev()
381 if wdirrev != nullrev:
386 if wdirrev != nullrev:
382 relevant += revset.baseset({wdirrev})
387 relevant += revset.baseset({wdirrev})
383
388
384 return subset & relevant
389 return subset & relevant
385
390
386 @showview('work', csettopic='work')
391 @showview('work', csettopic='work')
387 def showwork(ui, repo, displayer):
392 def showwork(ui, repo, displayer):
388 """changesets that aren't finished"""
393 """changesets that aren't finished"""
389 # TODO support date-based limiting when calling revset.
394 # TODO support date-based limiting when calling revset.
390 revs = repo.revs('sort(_underway(), topo)')
395 revs = repo.revs('sort(_underway(), topo)')
391
396
392 revdag = graphmod.dagwalker(repo, revs)
397 revdag = graphmod.dagwalker(repo, revs)
393
398
394 ui.setconfig('experimental', 'graphshorten', True)
399 ui.setconfig('experimental', 'graphshorten', True)
395 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
400 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
396
401
397 def extsetup(ui):
402 def extsetup(ui):
398 # Alias `hg <prefix><view>` to `hg show <view>`.
403 # Alias `hg <prefix><view>` to `hg show <view>`.
399 for prefix in ui.configlist('commands', 'show.aliasprefix'):
404 for prefix in ui.configlist('commands', 'show.aliasprefix'):
400 for view in showview._table:
405 for view in showview._table:
401 name = '%s%s' % (prefix, view)
406 name = '%s%s' % (prefix, view)
402
407
403 choice, allcommands = cmdutil.findpossible(name, commands.table,
408 choice, allcommands = cmdutil.findpossible(name, commands.table,
404 strict=True)
409 strict=True)
405
410
406 # This alias is already a command name. Don't set it.
411 # This alias is already a command name. Don't set it.
407 if name in choice:
412 if name in choice:
408 continue
413 continue
409
414
410 # Same for aliases.
415 # Same for aliases.
411 if ui.config('alias', name):
416 if ui.config('alias', name):
412 continue
417 continue
413
418
414 ui.setconfig('alias', name, 'show %s' % view, source='show')
419 ui.setconfig('alias', name, 'show %s' % view, source='show')
415
420
416 # Adjust the docstring of the show command so it shows all registered views.
421 # 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
422 # 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
423 # into core or when another extension wants to provide a view, we'll need
419 # to do this more robustly.
424 # to do this more robustly.
420 # TODO make this more robust.
425 # TODO make this more robust.
421 def _updatedocstring():
426 def _updatedocstring():
422 longest = max(map(len, showview._table.keys()))
427 longest = max(map(len, showview._table.keys()))
423 entries = []
428 entries = []
424 for key in sorted(showview._table.keys()):
429 for key in sorted(showview._table.keys()):
425 entries.append(pycompat.sysstr(' %s %s' % (
430 entries.append(pycompat.sysstr(' %s %s' % (
426 key.ljust(longest), showview._table[key]._origdoc)))
431 key.ljust(longest), showview._table[key]._origdoc)))
427
432
428 cmdtable['show'][0].__doc__ = pycompat.sysstr('%s\n\n%s\n ') % (
433 cmdtable['show'][0].__doc__ = pycompat.sysstr('%s\n\n%s\n ') % (
429 cmdtable['show'][0].__doc__.rstrip(),
434 cmdtable['show'][0].__doc__.rstrip(),
430 pycompat.sysstr('\n\n').join(entries))
435 pycompat.sysstr('\n\n').join(entries))
431
436
432 _updatedocstring()
437 _updatedocstring()
General Comments 0
You need to be logged in to leave comments. Login now