# HG changeset patch # User Gregory Szorc <gregory.sz...@gmail.com> # Date 1489378362 25200 # Sun Mar 12 21:12:42 2017 -0700 # Node ID d30057d358076cbe7d632cd573095af97543f932 # Parent 1c3352d7eaf24533ad52d4b8a024211e9189fb0b show: new extension for displaying various repository data
Currently, Mercurial has a number of commands to show information. And, there are features coming down the pipe that will introduce more commands for showing information. Currently, when introducing a new class of data or a view that we wish to expose to the user, the strategy is to introduce a new command or overload an existing command, sometimes both. For example, there is a desire to formalize the wip/smartlog/underway/mine functionality that many have devised. There is also a desire to introduce a "topics" concept. Others would like views of "the current stack." In the current model, we'd need a new command for wip/smartlog/etc (that behaves a lot like a pre-defined alias of `hg log`). For topics, we'd likely overload `hg topic[s]` to both display and manipulate topics. Adding new commands for every pre-defined query doesn't scale well and pollutes `hg help`. Overloading commands to perform read-only and write operations is arguably an UX anti-pattern: while having all functionality for a given concept in one command is nice, having a single command doing multiple discrete operations is not. Furthermore, a user may be surprised that a command they thought was read-only actually changes something. We discussed this at the Mercurial 4.0 Sprint in Paris and decided that having a single command where we could hang pre-defined views of various data would be a good idea. Having such a command would: * Help prevent an explosion of new query-related commands * Create a clear separation between read and write operations (mitigates footguns) * Avoids overloading the meaning of commands that manipulate data (bookmark, tag, branch, etc) (while we can't take away the existing behavior for BC reasons, we now won't introduce this behavior on new commands) * Allows users to discover informational views more easily by aggregating them in a single location * Lowers the barrier to creating the new views (since the barrier to creating a top-level command is relatively high) So, this commit introduces the `hg show` command via the "show" extension. This command accepts a positional argument of the "view" to show. New views can be registered with a decorator. To prove it works, we implement the "bookmarks" view, which shows a table of bookmarks and their associated nodes. We introduce a new style to hold everything used by `hg show`. For our initial bookmarks view, the output varies from `hg bookmarks`: * Padding is performed in the template itself as opposed to Python * Revision integers are not shown * shortest() is used to display a 5 character node by default (as opposed to static 12 characters) I chose to implement the "bookmarks" view first because it is simple and shouldn't invite too much bikeshedding that detracts from the evaluation of `hg show` itself. But there is an important point to consider: we now have 2 ways to show a list of bookmarks. I'm not a fan of introducing multiple ways to do very similar things. So it might be worth discussing how we wish to tackle this issue for bookmarks, tags, branches, MQ series, etc. I also made the choice of explicitly declaring the default show template not part of the standard BC guarantees. History has shown that we make mistakes and poor choices with output formatting but can't fix these mistakes later because random tools are parsing output and we don't want to break these tools. Optimizing for human consumption is one of my goals for `hg show`. So, by not covering the formatting as part of BC, the barrier to future change is much lower and humans benefit. At the aforementioned Sprint, we discussed and discarded various alternatives to `hg show`. We considered making `hg log <view>` perform this behavior. The main reason we can't do this is because a positional argument to `hg log` can be a file path and if there is a conflict between a path name and a view name, behavior is ambiguous. We could have introduced `hg log --view` or similar, but we felt that required too much typing (we don't want to require a command flag to show a view) and wasn't very discoverable. Furthermore, `hg log` is optimized for showing changelog data and there are things that `hg display` could display that aren't changelog centric. There were concerns about using "show" as the command name. Some users already have a "show" alias that is similar to `hg export`. There were also concerns that Git users adapted to `git show` would be confused by `hg show`'s different behavior. The main difference here is `git show` prints an `hg export` like view of the current commit by default and `hg show` requires an argument. `git show` can also display any Git object. `git show` does not support displaying more complex views: just single objects. If we implemented `hg show <hash>` or `hg show <identifier>`, `hg show` would be a superset of `git show`. Although, I'm hesitant to do that at this time because I view `hg show` as a higher-level querying command and there are namespace collisions between valid identifiers and registered views. There is also a prefix collision with `hg showconfig`, which is an alias of `hg config`. We also considered `hg view`, but that is already used by the "hgk" extension. `hg display` was also proposed at one point. It has a prefix collision with `hg diff`. General consensus was "show" or "view" are the best verbs. And since "view" was taken, "show" was chosen. diff --git a/contrib/wix/templates.wxs b/contrib/wix/templates.wxs --- a/contrib/wix/templates.wxs +++ b/contrib/wix/templates.wxs @@ -32,6 +32,7 @@ <File Name="map-cmdline.changelog" KeyPath="yes" /> <File Name="map-cmdline.compact" /> <File Name="map-cmdline.default" /> + <File Name="map-cmdline.show" /> <File Name="map-cmdline.bisect" /> <File Name="map-cmdline.xml" /> <File Name="map-cmdline.status" /> diff --git a/hgext/show.py b/hgext/show.py new file mode 100644 --- /dev/null +++ b/hgext/show.py @@ -0,0 +1,116 @@ +# show.py - Extension implementing `hg show` +# +# Copyright 2017 Gregory Szorc <gregory.sz...@gmail.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +"""unified command to show various repository information (EXPERIMENTAL) + +This extension provides the :hg:`show` command, which provides a central +command for displaying commonly-accessed repository data and views of that +data. +""" + +from __future__ import absolute_import + +from mercurial.i18n import _ +from mercurial import ( + cmdutil, + commands, + error, + registrar, +) + +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for +# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should +# be specifying the version(s) of Mercurial they are tested with, or +# leave the attribute unspecified. +testedwith = 'ships-with-hg-core' + +cmdtable = {} +command = cmdutil.command(cmdtable) + +class showcmdfunc(registrar._funcregistrarbase): + """Register a function to be invoked for an `hg show <thing>`.""" + _docformat = '%s -- %s' + + def _extrasetup(self, name, func, formatter=None): + func._formatter = formatter + +showview = showcmdfunc() + +@command('show', commands.formatteropts, _('[VIEW]')) +def show(ui, repo, view=None, template=None): + """show various repository information + + A requested view of repository data is displayed. + + If no view is requested, the list of available views is shown. + + .. note:: + + The default output from this command is not covered under Mercurial's + default backwards-compatible mechanism (which puts an emphasis on + not changing behavior). This means output from this command may change + in any future release. However, the values fed to the formatter are + covered under the default backwards-compatible mechanism. + + Automated consumers of this command should specify an explicit template + via ``-T/--template`` to guarantee output is stable. + + List of available views: + + """ + views = showview._table + + if not view: + ui.write(_('available views:\n')) + ui.write('\n') + + for name, func in sorted(views.items()): + ui.write(_('%s\n') % func.__doc__) + + ui.write('\n') + raise error.Abort(_('no view requested'), + hint=_('use `hg show <view>` to choose a view')) + + if view not in views: + raise error.Abort(_('unknown view: %s') % view, + hint=_('run `hg show` to see available views')) + + template = template or 'show' + fmtopic = views[view]._formatter + formatter = ui.formatter(fmtopic, {'template': template}) + + return views[view](ui, repo, formatter) + +@showview('bookmarks', formatter='bookmarks') +def showbookmarks(ui, repo, fm): + """bookmarks and their associated changeset""" + marks = repo._bookmarks + if not len(marks): + ui.write_err('(no bookmarks set)\n') + return 0 + + active = repo._activebookmark + longest = max(len(b) for b in marks) + + for bm, node in sorted(marks.items()): + fm.startitem() + fm.write('bookmark', '%s', bm) + fm.write('node', fm.hexfunc(node), fm.hexfunc(node)) + fm.data(ctx=repo[node], + active=bm == active, + longestlen=longest) + + fm.end() + +# Adjust the docstring of the show command so it shows all registered views. +# This is a bit hacky because it runs at the end of module load. When moved +# into core or when another extension wants to provide a view, we'll need +# to do this more robustly. +longest = max(map(len, showview._table.keys())) +for key in sorted(showview._table.keys()): + cmdtable['show'][0].__doc__ += ' %s %s\n' % ( + key.ljust(longest), showview._table[key]._origdoc) diff --git a/mercurial/templates/map-cmdline.show b/mercurial/templates/map-cmdline.show new file mode 100644 --- /dev/null +++ b/mercurial/templates/map-cmdline.show @@ -0,0 +1,2 @@ +# TODO add label() once interaction between pad() and label() is sorted out. +bookmarks = '{if(active, "*", " ")} {pad(bookmark, longestlen + 4)}{shortest(node, 5)}\n' diff --git a/tests/test-command-template.t b/tests/test-command-template.t --- a/tests/test-command-template.t +++ b/tests/test-command-template.t @@ -1108,11 +1108,11 @@ Error if no style: $ hg log --style notexist abort: style 'notexist' not found - (available styles: bisect, changelog, compact, default, phases, status, xml) + (available styles: bisect, changelog, compact, default, phases, show, status, xml) [255] $ hg log -T list - available styles: bisect, changelog, compact, default, phases, status, xml + available styles: bisect, changelog, compact, default, phases, show, status, xml abort: specify a template [255] diff --git a/tests/test-log.t b/tests/test-log.t --- a/tests/test-log.t +++ b/tests/test-log.t @@ -148,7 +148,7 @@ log on directory $ hg log -f -l1 --style something abort: style 'something' not found - (available styles: bisect, changelog, compact, default, phases, status, xml) + (available styles: bisect, changelog, compact, default, phases, show, status, xml) [255] -f, phases style diff --git a/tests/test-show.t b/tests/test-show.t new file mode 100644 --- /dev/null +++ b/tests/test-show.t @@ -0,0 +1,83 @@ + $ cat >> $HGRCPATH << EOF + > [extensions] + > show = + > EOF + +No arguments shows available views + + $ hg init empty + $ cd empty + $ hg show + available views: + + bookmarks -- bookmarks and their associated changeset + + abort: no view requested + (use `hg show <view>` to choose a view) + [255] + +`hg help show` prints available views + + $ hg help show + hg show [VIEW] + + show various repository information + + A requested view of repository data is displayed. + + If no view is requested, the list of available views is shown. + + Note: + The default output from this command is not covered under Mercurial's + default backwards-compatible mechanism (which puts an emphasis on not + changing behavior). This means output from this command may change in + any future release. However, the values fed to the formatter are + covered under the default backwards-compatible mechanism. + + Automated consumers of this command should specify an explicit template + via "-T/--template" to guarantee output is stable. + + List of available views: + + bookmarks bookmarks and their associated changeset + + (use 'hg help -e show' to show help for the show extension) + + options: + + (some details hidden, use --verbose to show complete help) + +Unknown view prints error + + $ hg show badview + abort: unknown view: badview + (run `hg show` to see available views) + [255] + + $ cd .. + +bookmarks view shows bookmarks in an aligned table + + $ hg init books + $ cd books + $ touch f0 + $ hg -q commit -A -m initial + $ echo book1 > f0 + $ hg commit -m 'commit for book1' + $ echo book2 > f0 + $ hg commit -m 'commit for book2' + + $ hg bookmark -r 1 book1 + $ hg bookmark a-longer-bookmark + + $ hg show bookmarks + * a-longer-bookmark 7b570 + book1 b757f + +A custom bookmarks template works + + $ hg show bookmarks -T '{node} {bookmark} {active}\n' + 7b5709ab64cbc34da9b4367b64afff47f2c4ee83 a-longer-bookmark True + b757f780b8ffd71267c6ccb32e0882d9d32a8cc0 book1 False + + $ cd .. _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel