86 |
86 |
87 showview = showcmdfunc() |
87 showview = showcmdfunc() |
88 |
88 |
89 |
89 |
90 @command( |
90 @command( |
91 'show', |
91 b'show', |
92 [ |
92 [ |
93 # TODO: Switch this template flag to use cmdutil.formatteropts if |
93 # TODO: Switch this template flag to use cmdutil.formatteropts if |
94 # 'hg show' becomes stable before --template/-T is stable. For now, |
94 # 'hg show' becomes stable before --template/-T is stable. For now, |
95 # we are putting it here without the '(EXPERIMENTAL)' flag because it |
95 # we are putting it here without the '(EXPERIMENTAL)' flag because it |
96 # is an important part of the 'hg show' user experience and the entire |
96 # is an important part of the 'hg show' user experience and the entire |
97 # 'hg show' experience is experimental. |
97 # 'hg show' experience is experimental. |
98 ('T', 'template', '', 'display with template', _('TEMPLATE')), |
98 (b'T', b'template', b'', b'display with template', _(b'TEMPLATE')), |
99 ], |
99 ], |
100 _('VIEW'), |
100 _(b'VIEW'), |
101 helpcategory=command.CATEGORY_CHANGE_NAVIGATION, |
101 helpcategory=command.CATEGORY_CHANGE_NAVIGATION, |
102 ) |
102 ) |
103 def show(ui, repo, view=None, template=None): |
103 def show(ui, repo, view=None, template=None): |
104 """show various repository information |
104 """show various repository information |
105 |
105 |
117 ``-T/--template``. |
117 ``-T/--template``. |
118 |
118 |
119 List of available views: |
119 List of available views: |
120 """ |
120 """ |
121 if ui.plain() and not template: |
121 if ui.plain() and not template: |
122 hint = _('invoke with -T/--template to control output format') |
122 hint = _(b'invoke with -T/--template to control output format') |
123 raise error.Abort(_('must specify a template in plain mode'), hint=hint) |
123 raise error.Abort( |
|
124 _(b'must specify a template in plain mode'), hint=hint |
|
125 ) |
124 |
126 |
125 views = showview._table |
127 views = showview._table |
126 |
128 |
127 if not view: |
129 if not view: |
128 ui.pager('show') |
130 ui.pager(b'show') |
129 # TODO consider using formatter here so available views can be |
131 # TODO consider using formatter here so available views can be |
130 # rendered to custom format. |
132 # rendered to custom format. |
131 ui.write(_('available views:\n')) |
133 ui.write(_(b'available views:\n')) |
132 ui.write('\n') |
134 ui.write(b'\n') |
133 |
135 |
134 for name, func in sorted(views.items()): |
136 for name, func in sorted(views.items()): |
135 ui.write('%s\n' % pycompat.sysbytes(func.__doc__)) |
137 ui.write(b'%s\n' % pycompat.sysbytes(func.__doc__)) |
136 |
138 |
137 ui.write('\n') |
139 ui.write(b'\n') |
138 raise error.Abort( |
140 raise error.Abort( |
139 _('no view requested'), |
141 _(b'no view requested'), |
140 hint=_('use "hg show VIEW" to choose a view'), |
142 hint=_(b'use "hg show VIEW" to choose a view'), |
141 ) |
143 ) |
142 |
144 |
143 # TODO use same logic as dispatch to perform prefix matching. |
145 # TODO use same logic as dispatch to perform prefix matching. |
144 if view not in views: |
146 if view not in views: |
145 raise error.Abort( |
147 raise error.Abort( |
146 _('unknown view: %s') % view, |
148 _(b'unknown view: %s') % view, |
147 hint=_('run "hg show" to see available views'), |
149 hint=_(b'run "hg show" to see available views'), |
148 ) |
150 ) |
149 |
151 |
150 template = template or 'show' |
152 template = template or b'show' |
151 |
153 |
152 fn = views[view] |
154 fn = views[view] |
153 ui.pager('show') |
155 ui.pager(b'show') |
154 |
156 |
155 if fn._fmtopic: |
157 if fn._fmtopic: |
156 fmtopic = 'show%s' % fn._fmtopic |
158 fmtopic = b'show%s' % fn._fmtopic |
157 with ui.formatter(fmtopic, {'template': template}) as fm: |
159 with ui.formatter(fmtopic, {b'template': template}) as fm: |
158 return fn(ui, repo, fm) |
160 return fn(ui, repo, fm) |
159 elif fn._csettopic: |
161 elif fn._csettopic: |
160 ref = 'show%s' % fn._csettopic |
162 ref = b'show%s' % fn._csettopic |
161 spec = formatter.lookuptemplate(ui, ref, template) |
163 spec = formatter.lookuptemplate(ui, ref, template) |
162 displayer = logcmdutil.changesettemplater(ui, repo, spec, buffered=True) |
164 displayer = logcmdutil.changesettemplater(ui, repo, spec, buffered=True) |
163 return fn(ui, repo, displayer) |
165 return fn(ui, repo, displayer) |
164 else: |
166 else: |
165 return fn(ui, repo) |
167 return fn(ui, repo) |
166 |
168 |
167 |
169 |
168 @showview('bookmarks', fmtopic='bookmarks') |
170 @showview(b'bookmarks', fmtopic=b'bookmarks') |
169 def showbookmarks(ui, repo, fm): |
171 def showbookmarks(ui, repo, fm): |
170 """bookmarks and their associated changeset""" |
172 """bookmarks and their associated changeset""" |
171 marks = repo._bookmarks |
173 marks = repo._bookmarks |
172 if not len(marks): |
174 if not len(marks): |
173 # This is a bit hacky. Ideally, templates would have a way to |
175 # This is a bit hacky. Ideally, templates would have a way to |
174 # specify an empty output, but we shouldn't corrupt JSON while |
176 # specify an empty output, but we shouldn't corrupt JSON while |
175 # waiting for this functionality. |
177 # waiting for this functionality. |
176 if not isinstance(fm, formatter.jsonformatter): |
178 if not isinstance(fm, formatter.jsonformatter): |
177 ui.write(_('(no bookmarks set)\n')) |
179 ui.write(_(b'(no bookmarks set)\n')) |
178 return |
180 return |
179 |
181 |
180 revs = [repo[node].rev() for node in marks.values()] |
182 revs = [repo[node].rev() for node in marks.values()] |
181 active = repo._activebookmark |
183 active = repo._activebookmark |
182 longestname = max(len(b) for b in marks) |
184 longestname = max(len(b) for b in marks) |
183 nodelen = longestshortest(repo, revs) |
185 nodelen = longestshortest(repo, revs) |
184 |
186 |
185 for bm, node in sorted(marks.items()): |
187 for bm, node in sorted(marks.items()): |
186 fm.startitem() |
188 fm.startitem() |
187 fm.context(ctx=repo[node]) |
189 fm.context(ctx=repo[node]) |
188 fm.write('bookmark', '%s', bm) |
190 fm.write(b'bookmark', b'%s', bm) |
189 fm.write('node', fm.hexfunc(node), fm.hexfunc(node)) |
191 fm.write(b'node', fm.hexfunc(node), fm.hexfunc(node)) |
190 fm.data( |
192 fm.data( |
191 active=bm == active, longestbookmarklen=longestname, nodelen=nodelen |
193 active=bm == active, longestbookmarklen=longestname, nodelen=nodelen |
192 ) |
194 ) |
193 |
195 |
194 |
196 |
195 @showview('stack', csettopic='stack') |
197 @showview(b'stack', csettopic=b'stack') |
196 def showstack(ui, repo, displayer): |
198 def showstack(ui, repo, displayer): |
197 """current line of work""" |
199 """current line of work""" |
198 wdirctx = repo['.'] |
200 wdirctx = repo[b'.'] |
199 if wdirctx.rev() == nullrev: |
201 if wdirctx.rev() == nullrev: |
200 raise error.Abort( |
202 raise error.Abort( |
201 _('stack view only available when there is a ' 'working directory') |
203 _( |
|
204 b'stack view only available when there is a ' |
|
205 b'working directory' |
|
206 ) |
202 ) |
207 ) |
203 |
208 |
204 if wdirctx.phase() == phases.public: |
209 if wdirctx.phase() == phases.public: |
205 ui.write( |
210 ui.write( |
206 _( |
211 _( |
207 '(empty stack; working directory parent is a published ' |
212 b'(empty stack; working directory parent is a published ' |
208 'changeset)\n' |
213 b'changeset)\n' |
209 ) |
214 ) |
210 ) |
215 ) |
211 return |
216 return |
212 |
217 |
213 # TODO extract "find stack" into a function to facilitate |
218 # TODO extract "find stack" into a function to facilitate |
305 |
310 |
306 for i, rev in enumerate(sortedheads): |
311 for i, rev in enumerate(sortedheads): |
307 ctx = repo[rev] |
312 ctx = repo[rev] |
308 |
313 |
309 if i: |
314 if i: |
310 ui.write(': ') |
315 ui.write(b': ') |
311 else: |
316 else: |
312 ui.write(' ') |
317 ui.write(b' ') |
313 |
318 |
314 ui.write('o ') |
319 ui.write(b'o ') |
315 displayer.show(ctx, nodelen=nodelen) |
320 displayer.show(ctx, nodelen=nodelen) |
316 displayer.flush(ctx) |
321 displayer.flush(ctx) |
317 ui.write('\n') |
322 ui.write(b'\n') |
318 |
323 |
319 if i: |
324 if i: |
320 ui.write(':/') |
325 ui.write(b':/') |
321 else: |
326 else: |
322 ui.write(' /') |
327 ui.write(b' /') |
323 |
328 |
324 ui.write(' (') |
329 ui.write(b' (') |
325 ui.write( |
330 ui.write( |
326 _('%d commits ahead') % revdistance[rev], |
331 _(b'%d commits ahead') % revdistance[rev], |
327 label='stack.commitdistance', |
332 label=b'stack.commitdistance', |
328 ) |
333 ) |
329 |
334 |
330 if haverebase: |
335 if haverebase: |
331 # TODO may be able to omit --source in some scenarios |
336 # TODO may be able to omit --source in some scenarios |
332 ui.write('; ') |
337 ui.write(b'; ') |
333 ui.write( |
338 ui.write( |
334 ( |
339 ( |
335 'hg rebase --source %s --dest %s' |
340 b'hg rebase --source %s --dest %s' |
336 % (shortest(sourcectx), shortest(ctx)) |
341 % (shortest(sourcectx), shortest(ctx)) |
337 ), |
342 ), |
338 label='stack.rebasehint', |
343 label=b'stack.rebasehint', |
339 ) |
344 ) |
340 |
345 |
341 ui.write(')\n') |
346 ui.write(b')\n') |
342 |
347 |
343 ui.write(':\n: ') |
348 ui.write(b':\n: ') |
344 ui.write(_('(stack head)\n'), label='stack.label') |
349 ui.write(_(b'(stack head)\n'), label=b'stack.label') |
345 |
350 |
346 if branchpointattip: |
351 if branchpointattip: |
347 ui.write(' \\ / ') |
352 ui.write(b' \\ / ') |
348 ui.write(_('(multiple children)\n'), label='stack.label') |
353 ui.write(_(b'(multiple children)\n'), label=b'stack.label') |
349 ui.write(' |\n') |
354 ui.write(b' |\n') |
350 |
355 |
351 for rev in stackrevs: |
356 for rev in stackrevs: |
352 ctx = repo[rev] |
357 ctx = repo[rev] |
353 symbol = '@' if rev == wdirctx.rev() else 'o' |
358 symbol = b'@' if rev == wdirctx.rev() else b'o' |
354 |
359 |
355 if newheads: |
360 if newheads: |
356 ui.write(': ') |
361 ui.write(b': ') |
357 else: |
362 else: |
358 ui.write(' ') |
363 ui.write(b' ') |
359 |
364 |
360 ui.write(symbol, ' ') |
365 ui.write(symbol, b' ') |
361 displayer.show(ctx, nodelen=nodelen) |
366 displayer.show(ctx, nodelen=nodelen) |
362 displayer.flush(ctx) |
367 displayer.flush(ctx) |
363 ui.write('\n') |
368 ui.write(b'\n') |
364 |
369 |
365 # TODO display histedit hint? |
370 # TODO display histedit hint? |
366 |
371 |
367 if basectx: |
372 if basectx: |
368 # Vertically and horizontally separate stack base from parent |
373 # Vertically and horizontally separate stack base from parent |
369 # to reinforce stack boundary. |
374 # to reinforce stack boundary. |
370 if newheads: |
375 if newheads: |
371 ui.write(':/ ') |
376 ui.write(b':/ ') |
372 else: |
377 else: |
373 ui.write(' / ') |
378 ui.write(b' / ') |
374 |
379 |
375 ui.write(_('(stack base)'), '\n', label='stack.label') |
380 ui.write(_(b'(stack base)'), b'\n', label=b'stack.label') |
376 ui.write('o ') |
381 ui.write(b'o ') |
377 |
382 |
378 displayer.show(basectx, nodelen=nodelen) |
383 displayer.show(basectx, nodelen=nodelen) |
379 displayer.flush(basectx) |
384 displayer.flush(basectx) |
380 ui.write('\n') |
385 ui.write(b'\n') |
381 |
386 |
382 |
387 |
383 @revsetpredicate('_underway([commitage[, headage]])') |
388 @revsetpredicate(b'_underway([commitage[, headage]])') |
384 def underwayrevset(repo, subset, x): |
389 def underwayrevset(repo, subset, x): |
385 args = revset.getargsdict(x, 'underway', 'commitage headage') |
390 args = revset.getargsdict(x, b'underway', b'commitage headage') |
386 if 'commitage' not in args: |
391 if b'commitage' not in args: |
387 args['commitage'] = None |
392 args[b'commitage'] = None |
388 if 'headage' not in args: |
393 if b'headage' not in args: |
389 args['headage'] = None |
394 args[b'headage'] = None |
390 |
395 |
391 # We assume callers of this revset add a topographical sort on the |
396 # We assume callers of this revset add a topographical sort on the |
392 # result. This means there is no benefit to making the revset lazy |
397 # result. This means there is no benefit to making the revset lazy |
393 # since the topographical sort needs to consume all revs. |
398 # since the topographical sort needs to consume all revs. |
394 # |
399 # |
397 |
402 |
398 # Mutable changesets (non-public) are the most important changesets |
403 # Mutable changesets (non-public) are the most important changesets |
399 # to return. ``not public()`` will also pull in obsolete changesets if |
404 # to return. ``not public()`` will also pull in obsolete changesets if |
400 # there is a non-obsolete changeset with obsolete ancestors. This is |
405 # there is a non-obsolete changeset with obsolete ancestors. This is |
401 # why we exclude obsolete changesets from this query. |
406 # why we exclude obsolete changesets from this query. |
402 rs = 'not public() and not obsolete()' |
407 rs = b'not public() and not obsolete()' |
403 rsargs = [] |
408 rsargs = [] |
404 if args['commitage']: |
409 if args[b'commitage']: |
405 rs += ' and date(%s)' |
410 rs += b' and date(%s)' |
406 rsargs.append( |
411 rsargs.append( |
407 revsetlang.getstring( |
412 revsetlang.getstring( |
408 args['commitage'], _('commitage requires a string') |
413 args[b'commitage'], _(b'commitage requires a string') |
409 ) |
414 ) |
410 ) |
415 ) |
411 |
416 |
412 mutable = repo.revs(rs, *rsargs) |
417 mutable = repo.revs(rs, *rsargs) |
413 relevant = revset.baseset(mutable) |
418 relevant = revset.baseset(mutable) |
414 |
419 |
415 # Add parents of mutable changesets to provide context. |
420 # Add parents of mutable changesets to provide context. |
416 relevant += repo.revs('parents(%ld)', mutable) |
421 relevant += repo.revs(b'parents(%ld)', mutable) |
417 |
422 |
418 # We also pull in (public) heads if they a) aren't closing a branch |
423 # We also pull in (public) heads if they a) aren't closing a branch |
419 # b) are recent. |
424 # b) are recent. |
420 rs = 'head() and not closed()' |
425 rs = b'head() and not closed()' |
421 rsargs = [] |
426 rsargs = [] |
422 if args['headage']: |
427 if args[b'headage']: |
423 rs += ' and date(%s)' |
428 rs += b' and date(%s)' |
424 rsargs.append( |
429 rsargs.append( |
425 revsetlang.getstring( |
430 revsetlang.getstring( |
426 args['headage'], _('headage requires a string') |
431 args[b'headage'], _(b'headage requires a string') |
427 ) |
432 ) |
428 ) |
433 ) |
429 |
434 |
430 relevant += repo.revs(rs, *rsargs) |
435 relevant += repo.revs(rs, *rsargs) |
431 |
436 |
432 # Add working directory parent. |
437 # Add working directory parent. |
433 wdirrev = repo['.'].rev() |
438 wdirrev = repo[b'.'].rev() |
434 if wdirrev != nullrev: |
439 if wdirrev != nullrev: |
435 relevant += revset.baseset({wdirrev}) |
440 relevant += revset.baseset({wdirrev}) |
436 |
441 |
437 return subset & relevant |
442 return subset & relevant |
438 |
443 |
439 |
444 |
440 @showview('work', csettopic='work') |
445 @showview(b'work', csettopic=b'work') |
441 def showwork(ui, repo, displayer): |
446 def showwork(ui, repo, displayer): |
442 """changesets that aren't finished""" |
447 """changesets that aren't finished""" |
443 # TODO support date-based limiting when calling revset. |
448 # TODO support date-based limiting when calling revset. |
444 revs = repo.revs('sort(_underway(), topo)') |
449 revs = repo.revs(b'sort(_underway(), topo)') |
445 nodelen = longestshortest(repo, revs) |
450 nodelen = longestshortest(repo, revs) |
446 |
451 |
447 revdag = graphmod.dagwalker(repo, revs) |
452 revdag = graphmod.dagwalker(repo, revs) |
448 |
453 |
449 ui.setconfig('experimental', 'graphshorten', True) |
454 ui.setconfig(b'experimental', b'graphshorten', True) |
450 logcmdutil.displaygraph( |
455 logcmdutil.displaygraph( |
451 ui, |
456 ui, |
452 repo, |
457 repo, |
453 revdag, |
458 revdag, |
454 displayer, |
459 displayer, |
455 graphmod.asciiedges, |
460 graphmod.asciiedges, |
456 props={'nodelen': nodelen}, |
461 props={b'nodelen': nodelen}, |
457 ) |
462 ) |
458 |
463 |
459 |
464 |
460 def extsetup(ui): |
465 def extsetup(ui): |
461 # Alias `hg <prefix><view>` to `hg show <view>`. |
466 # Alias `hg <prefix><view>` to `hg show <view>`. |
462 for prefix in ui.configlist('commands', 'show.aliasprefix'): |
467 for prefix in ui.configlist(b'commands', b'show.aliasprefix'): |
463 for view in showview._table: |
468 for view in showview._table: |
464 name = '%s%s' % (prefix, view) |
469 name = b'%s%s' % (prefix, view) |
465 |
470 |
466 choice, allcommands = cmdutil.findpossible( |
471 choice, allcommands = cmdutil.findpossible( |
467 name, commands.table, strict=True |
472 name, commands.table, strict=True |
468 ) |
473 ) |
469 |
474 |
470 # This alias is already a command name. Don't set it. |
475 # This alias is already a command name. Don't set it. |
471 if name in choice: |
476 if name in choice: |
472 continue |
477 continue |
473 |
478 |
474 # Same for aliases. |
479 # Same for aliases. |
475 if ui.config('alias', name, None): |
480 if ui.config(b'alias', name, None): |
476 continue |
481 continue |
477 |
482 |
478 ui.setconfig('alias', name, 'show %s' % view, source='show') |
483 ui.setconfig(b'alias', name, b'show %s' % view, source=b'show') |
479 |
484 |
480 |
485 |
481 def longestshortest(repo, revs, minlen=4): |
486 def longestshortest(repo, revs, minlen=4): |
482 """Return the length of the longest shortest node to identify revisions. |
487 """Return the length of the longest shortest node to identify revisions. |
483 |
488 |