Patchwork [2,of,6,V3] hgweb: add revsetsearch() function when query can be parsed as a revset

login
register
mail settings
Submitter Alexander Plavin
Date Aug. 22, 2013, 3:11 p.m.
Message ID <c9ded075156603c15bb0.1377184273@debian-alexander.dolgopa>
Download mbox | patch
Permalink /patch/2244/
State Deferred
Headers show

Comments

Alexander Plavin - Aug. 22, 2013, 3:11 p.m.
# HG changeset patch
# User Alexander Plavin <alexander@plav.in>
# Date 1375823774 -14400
#      Wed Aug 07 01:16:14 2013 +0400
# Node ID c9ded075156603c15bb0a71273d2babbd19ab6fb
# Parent  f32bf1774cc3e6cd7ee99a7be2724daa23223a98
hgweb: add revsetsearch() function when query can be parsed as a revset

This function is used when all the conditions are met:
- 'reverse(%s)' % query string can be parsed to a revset tree
- this tree has depth more than two, i.e. the query has some part of
revset syntax used
- the repo can be actually matched against this tree, i.e. it has only existent
function/operators and revisions/tags/bookmarks specified are correct

Otherwise keywordsearch() or revsearch() functions are used as before.

Add several new tests for different parsing conditions and exception handling.
Augie Fackler - Aug. 26, 2013, 2:47 p.m.
On Thu, Aug 22, 2013 at 07:11:13PM +0400, Alexander Plavin wrote:
> # HG changeset patch
> # User Alexander Plavin <alexander@plav.in>
> # Date 1375823774 -14400
> #      Wed Aug 07 01:16:14 2013 +0400
> # Node ID c9ded075156603c15bb0a71273d2babbd19ab6fb
> # Parent  f32bf1774cc3e6cd7ee99a7be2724daa23223a98
> hgweb: add revsetsearch() function when query can be parsed as a revset
>
> This function is used when all the conditions are met:
> - 'reverse(%s)' % query string can be parsed to a revset tree
> - this tree has depth more than two, i.e. the query has some part of
> revset syntax used
> - the repo can be actually matched against this tree, i.e. it has only existent
> function/operators and revisions/tags/bookmarks specified are correct
>
> Otherwise keywordsearch() or revsearch() functions are used as before.
>
> Add several new tests for different parsing conditions and exception handling.

Looks great.

>
> diff -r f32bf1774cc3 -r c9ded0751566 mercurial/hgweb/webcommands.py
> --- a/mercurial/hgweb/webcommands.py	Fri Aug 09 22:52:58 2013 +0400
> +++ b/mercurial/hgweb/webcommands.py	Wed Aug 07 01:16:14 2013 +0400
> @@ -16,6 +16,8 @@
>  from mercurial import help as helpmod
>  from mercurial import scmutil
>  from mercurial.i18n import _
> +from mercurial.error import ParseError, RepoLookupError, Abort
> +from mercurial import parser, revset
>
>  # __all__ is populated with the allowed commands. Be sure to add to it if
>  # you're adding a new command, or the new command won't work.
> @@ -141,18 +143,50 @@
>
>              yield ctx
>
> +    def revsetsearch(revdef):
> +        revs = revset.match(web.repo.ui, revdef)(web.repo, list(web.repo))
> +        for r in revs:
> +            yield web.repo[r]
> +
>      searchfuncs = {
>          'rev': revsearch,
>          'kw': keywordsearch,
> +        'revset': revsetsearch,
>      }
>
>      def getsearchmode(query):
>          try:
>              ctx = web.repo[query]
>          except (error.RepoError, error.LookupError):
> +            # query is not an exact revision pointer, need to
> +            # decide if it's a revset expession or keywords
> +            pass
> +        else:
> +            return 'rev', ctx

Why not just return from the try block instead of using try/else?

> +
> +        revdef = 'reverse(%s)' % query
> +        try:
> +            p = parser.parser(revset.tokenize, revset.elements)
> +            tree, pos = p.parse(revdef)
> +        except ParseError:
> +            # can't parse to a revset tree
> +            return 'kw', query
> +
> +        if revset.depth(tree) <= 2:
> +            # no revset syntax used
> +            return 'kw', query
> +
> +        mfunc = revset.match(None, revdef)
> +        try:
> +            # try running against empty subset
> +            mfunc(web.repo, [])
> +            # ParseError: wrongly placed tokens, wrongs arguments, etc
> +            # RepoLookupError: no such revision, e.g. in 'revision:'
> +            # Abort: bookmark/tag not exists
> +        except (ParseError, RepoLookupError, Abort):
>              return 'kw', query
>          else:
> -            return 'rev', ctx
> +            return 'revset', revdef
>
>      def changelist(**map):
>          count = 0
> diff -r f32bf1774cc3 -r c9ded0751566 tests/test-hgweb-commands.t
> --- a/tests/test-hgweb-commands.t	Fri Aug 09 22:52:58 2013 +0400
> +++ b/tests/test-hgweb-commands.t	Wed Aug 07 01:16:14 2013 +0400
> @@ -537,6 +537,101 @@
>    $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=stable&style=raw' | grep 'revision:'
>    revision:    2
>
> +Search with revset syntax
> +
> +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=tip^&style=raw'
> +  200 Script output follows
> +
> +
> +  # HG changesets search
> +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> +  # Query "tip^"
> +
> +  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
> +  revision:    2
> +  user:        test
> +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> +  summary:     branch
> +  branch:      stable
> +
> +
> +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(),2)^&style=raw'
> +  200 Script output follows
> +
> +
> +  # HG changesets search
> +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> +  # Query "last(all(),2)^"
> +
> +  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
> +  revision:    2
> +  user:        test
> +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> +  summary:     branch
> +  branch:      stable
> +
> +  changeset:   a4f92ed23982be056b9852de5dfe873eaac7f0de
> +  revision:    1
> +  user:        test
> +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> +  summary:     Added tag 1.0 for changeset 2ef0ac749a14
> +  branch:      default
> +
> +
> +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(,2)^&style=raw'
> +  200 Script output follows
> +
> +
> +  # HG changesets search
> +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> +  # Query "last(all(,2)^"
> +
> +
> +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(al(),2)^&style=raw'
> +  200 Script output follows
> +
> +
> +  # HG changesets search
> +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> +  # Query "last(al(),2)^"
> +
> +
> +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(anotherthing)&style=raw'
> +  200 Script output follows
> +
> +
> +  # HG changesets search
> +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> +  # Query "bookmark(anotherthing)"
> +
> +  changeset:   2ef0ac749a14e4f57a5a822464a0902c6f7f448f
> +  revision:    0
> +  user:        test
> +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> +  summary:     base
> +  tag:         1.0
> +  bookmark:    anotherthing
> +
> +
> +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(abc)&style=raw'
> +  200 Script output follows
> +
> +
> +  # HG changesets search
> +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> +  # Query "bookmark(abc)"
> +
> +
> +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=deadbeef:&style=raw'
> +  200 Script output follows
> +
> +
> +  # HG changesets search
> +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> +  # Query "deadbeef:"
> +
> +
> +
>  File-related
>
>    $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/foo/?style=raw'
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
Alexander Plavin - Aug. 27, 2013, 7:34 a.m.
26.08.2013, 18:47, "Augie Fackler" <raf@durin42.com>:
> On Thu, Aug 22, 2013 at 07:11:13PM +0400, Alexander Plavin wrote:
>
>>  # HG changeset patch
>>  # User Alexander Plavin <alexander@plav.in>
>>  # Date 1375823774 -14400
>>  #      Wed Aug 07 01:16:14 2013 +0400
>>  # Node ID c9ded075156603c15bb0a71273d2babbd19ab6fb
>>  # Parent  f32bf1774cc3e6cd7ee99a7be2724daa23223a98
>>  hgweb: add revsetsearch() function when query can be parsed as a revset
>>
>>  This function is used when all the conditions are met:
>>  - 'reverse(%s)' % query string can be parsed to a revset tree
>>  - this tree has depth more than two, i.e. the query has some part of
>>  revset syntax used
>>  - the repo can be actually matched against this tree, i.e. it has only existent
>>  function/operators and revisions/tags/bookmarks specified are correct
>>
>>  Otherwise keywordsearch() or revsearch() functions are used as before.
>>
>>  Add several new tests for different parsing conditions and exception handling.
>
> Looks great.
>
>>  diff -r f32bf1774cc3 -r c9ded0751566 mercurial/hgweb/webcommands.py
>>  --- a/mercurial/hgweb/webcommands.py Fri Aug 09 22:52:58 2013 +0400
>>  +++ b/mercurial/hgweb/webcommands.py Wed Aug 07 01:16:14 2013 +0400
>>  @@ -16,6 +16,8 @@
>>   from mercurial import help as helpmod
>>   from mercurial import scmutil
>>   from mercurial.i18n import _
>>  +from mercurial.error import ParseError, RepoLookupError, Abort
>>  +from mercurial import parser, revset
>>
>>   # __all__ is populated with the allowed commands. Be sure to add to it if
>>   # you're adding a new command, or the new command won't work.
>>  @@ -141,18 +143,50 @@
>>
>>               yield ctx
>>
>>  +    def revsetsearch(revdef):
>>  +        revs = revset.match(web.repo.ui, revdef)(web.repo, list(web.repo))
>>  +        for r in revs:
>>  +            yield web.repo[r]
>>  +
>>       searchfuncs = {
>>           'rev': revsearch,
>>           'kw': keywordsearch,
>>  +        'revset': revsetsearch,
>>       }
>>
>>       def getsearchmode(query):
>>           try:
>>               ctx = web.repo[query]
>>           except (error.RepoError, error.LookupError):
>>  +            # query is not an exact revision pointer, need to
>>  +            # decide if it's a revset expession or keywords
>>  +            pass
>>  +        else:
>>  +            return 'rev', ctx
>
> Why not just return from the try block instead of using try/else?

try/except/else here is actually introduced in an earlier patch (https://hg.plav.in/hg_fork/rev/5407b1e23932), and as for me it looks more symmetrical ('except' and 'else' branches), thus easier to read. Am I wrong here?

>
>>  +
>>  +        revdef = 'reverse(%s)' % query
>>  +        try:
>>  +            p = parser.parser(revset.tokenize, revset.elements)
>>  +            tree, pos = p.parse(revdef)
>>  +        except ParseError:
>>  +            # can't parse to a revset tree
>>  +            return 'kw', query
>>  +
>>  +        if revset.depth(tree) <= 2:
>>  +            # no revset syntax used
>>  +            return 'kw', query
>>  +
>>  +        mfunc = revset.match(None, revdef)
>>  +        try:
>>  +            # try running against empty subset
>>  +            mfunc(web.repo, [])
>>  +            # ParseError: wrongly placed tokens, wrongs arguments, etc
>>  +            # RepoLookupError: no such revision, e.g. in 'revision:'
>>  +            # Abort: bookmark/tag not exists
>>  +        except (ParseError, RepoLookupError, Abort):
>>               return 'kw', query
>>           else:
>>  -            return 'rev', ctx
>>  +            return 'revset', revdef
>>
>>       def changelist(**map):
>>           count = 0
>>  diff -r f32bf1774cc3 -r c9ded0751566 tests/test-hgweb-commands.t
>>  --- a/tests/test-hgweb-commands.t Fri Aug 09 22:52:58 2013 +0400
>>  +++ b/tests/test-hgweb-commands.t Wed Aug 07 01:16:14 2013 +0400
>>  @@ -537,6 +537,101 @@
>>     $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=stable&style=raw' | grep 'revision:'
>>     revision:    2
>>
>>  +Search with revset syntax
>>  +
>>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=tip^&style=raw'
>>  +  200 Script output follows
>>  +
>>  +
>>  +  # HG changesets search
>>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
>>  +  # Query "tip^"
>>  +
>>  +  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
>>  +  revision:    2
>>  +  user:        test
>>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
>>  +  summary:     branch
>>  +  branch:      stable
>>  +
>>  +
>>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(),2)^&style=raw'
>>  +  200 Script output follows
>>  +
>>  +
>>  +  # HG changesets search
>>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
>>  +  # Query "last(all(),2)^"
>>  +
>>  +  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
>>  +  revision:    2
>>  +  user:        test
>>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
>>  +  summary:     branch
>>  +  branch:      stable
>>  +
>>  +  changeset:   a4f92ed23982be056b9852de5dfe873eaac7f0de
>>  +  revision:    1
>>  +  user:        test
>>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
>>  +  summary:     Added tag 1.0 for changeset 2ef0ac749a14
>>  +  branch:      default
>>  +
>>  +
>>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(,2)^&style=raw'
>>  +  200 Script output follows
>>  +
>>  +
>>  +  # HG changesets search
>>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
>>  +  # Query "last(all(,2)^"
>>  +
>>  +
>>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(al(),2)^&style=raw'
>>  +  200 Script output follows
>>  +
>>  +
>>  +  # HG changesets search
>>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
>>  +  # Query "last(al(),2)^"
>>  +
>>  +
>>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(anotherthing)&style=raw'
>>  +  200 Script output follows
>>  +
>>  +
>>  +  # HG changesets search
>>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
>>  +  # Query "bookmark(anotherthing)"
>>  +
>>  +  changeset:   2ef0ac749a14e4f57a5a822464a0902c6f7f448f
>>  +  revision:    0
>>  +  user:        test
>>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
>>  +  summary:     base
>>  +  tag:         1.0
>>  +  bookmark:    anotherthing
>>  +
>>  +
>>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(abc)&style=raw'
>>  +  200 Script output follows
>>  +
>>  +
>>  +  # HG changesets search
>>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
>>  +  # Query "bookmark(abc)"
>>  +
>>  +
>>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=deadbeef:&style=raw'
>>  +  200 Script output follows
>>  +
>>  +
>>  +  # HG changesets search
>>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
>>  +  # Query "deadbeef:"
>>  +
>>  +
>>  +
>>   File-related
>>
>>     $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/foo/?style=raw'
>>  _______________________________________________
>>  Mercurial-devel mailing list
>>  Mercurial-devel@selenic.com
>>  http://selenic.com/mailman/listinfo/mercurial-devel
Augie Fackler - Aug. 27, 2013, 3:07 p.m.
On Tue, Aug 27, 2013 at 11:34:51AM +0400, Alexander Plavin wrote:
>
>
> 26.08.2013, 18:47, "Augie Fackler" <raf@durin42.com>:
> > On Thu, Aug 22, 2013 at 07:11:13PM +0400, Alexander Plavin wrote:
> >
> >>  # HG changeset patch
> >>  # User Alexander Plavin <alexander@plav.in>
> >>  # Date 1375823774 -14400
> >>  #      Wed Aug 07 01:16:14 2013 +0400
> >>  # Node ID c9ded075156603c15bb0a71273d2babbd19ab6fb
> >>  # Parent  f32bf1774cc3e6cd7ee99a7be2724daa23223a98
> >>  hgweb: add revsetsearch() function when query can be parsed as a revset
> >>
> >>  This function is used when all the conditions are met:
> >>  - 'reverse(%s)' % query string can be parsed to a revset tree
> >>  - this tree has depth more than two, i.e. the query has some part of
> >>  revset syntax used
> >>  - the repo can be actually matched against this tree, i.e. it has only existent
> >>  function/operators and revisions/tags/bookmarks specified are correct
> >>
> >>  Otherwise keywordsearch() or revsearch() functions are used as before.
> >>
> >>  Add several new tests for different parsing conditions and exception handling.
> >
> > Looks great.
> >
> >>  diff -r f32bf1774cc3 -r c9ded0751566 mercurial/hgweb/webcommands.py
> >>  --- a/mercurial/hgweb/webcommands.py Fri Aug 09 22:52:58 2013 +0400
> >>  +++ b/mercurial/hgweb/webcommands.py Wed Aug 07 01:16:14 2013 +0400
> >>  @@ -16,6 +16,8 @@
> >>   from mercurial import help as helpmod
> >>   from mercurial import scmutil
> >>   from mercurial.i18n import _
> >>  +from mercurial.error import ParseError, RepoLookupError, Abort
> >>  +from mercurial import parser, revset
> >>
> >>   # __all__ is populated with the allowed commands. Be sure to add to it if
> >>   # you're adding a new command, or the new command won't work.
> >>  @@ -141,18 +143,50 @@
> >>
> >>               yield ctx
> >>
> >>  +    def revsetsearch(revdef):
> >>  +        revs = revset.match(web.repo.ui, revdef)(web.repo, list(web.repo))
> >>  +        for r in revs:
> >>  +            yield web.repo[r]
> >>  +
> >>       searchfuncs = {
> >>           'rev': revsearch,
> >>           'kw': keywordsearch,
> >>  +        'revset': revsetsearch,
> >>       }
> >>
> >>       def getsearchmode(query):
> >>           try:
> >>               ctx = web.repo[query]
> >>           except (error.RepoError, error.LookupError):
> >>  +            # query is not an exact revision pointer, need to
> >>  +            # decide if it's a revset expession or keywords
> >>  +            pass
> >>  +        else:
> >>  +            return 'rev', ctx
> >
> > Why not just return from the try block instead of using try/else?
>
> try/except/else here is actually introduced in an earlier patch (https://hg.plav.in/hg_fork/rev/5407b1e23932), and as for me it looks more symmetrical ('except' and 'else' branches), thus easier to read. Am I wrong here?
>

Doesn't bother me much one way or the other. I don't feel strongly.

> >
> >>  +
> >>  +        revdef = 'reverse(%s)' % query
> >>  +        try:
> >>  +            p = parser.parser(revset.tokenize, revset.elements)
> >>  +            tree, pos = p.parse(revdef)
> >>  +        except ParseError:
> >>  +            # can't parse to a revset tree
> >>  +            return 'kw', query
> >>  +
> >>  +        if revset.depth(tree) <= 2:
> >>  +            # no revset syntax used
> >>  +            return 'kw', query
> >>  +
> >>  +        mfunc = revset.match(None, revdef)
> >>  +        try:
> >>  +            # try running against empty subset
> >>  +            mfunc(web.repo, [])
> >>  +            # ParseError: wrongly placed tokens, wrongs arguments, etc
> >>  +            # RepoLookupError: no such revision, e.g. in 'revision:'
> >>  +            # Abort: bookmark/tag not exists
> >>  +        except (ParseError, RepoLookupError, Abort):
> >>               return 'kw', query
> >>           else:
> >>  -            return 'rev', ctx
> >>  +            return 'revset', revdef
> >>
> >>       def changelist(**map):
> >>           count = 0
> >>  diff -r f32bf1774cc3 -r c9ded0751566 tests/test-hgweb-commands.t
> >>  --- a/tests/test-hgweb-commands.t Fri Aug 09 22:52:58 2013 +0400
> >>  +++ b/tests/test-hgweb-commands.t Wed Aug 07 01:16:14 2013 +0400
> >>  @@ -537,6 +537,101 @@
> >>     $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=stable&style=raw' | grep 'revision:'
> >>     revision:    2
> >>
> >>  +Search with revset syntax
> >>  +
> >>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=tip^&style=raw'
> >>  +  200 Script output follows
> >>  +
> >>  +
> >>  +  # HG changesets search
> >>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> >>  +  # Query "tip^"
> >>  +
> >>  +  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
> >>  +  revision:    2
> >>  +  user:        test
> >>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> >>  +  summary:     branch
> >>  +  branch:      stable
> >>  +
> >>  +
> >>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(),2)^&style=raw'
> >>  +  200 Script output follows
> >>  +
> >>  +
> >>  +  # HG changesets search
> >>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> >>  +  # Query "last(all(),2)^"
> >>  +
> >>  +  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
> >>  +  revision:    2
> >>  +  user:        test
> >>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> >>  +  summary:     branch
> >>  +  branch:      stable
> >>  +
> >>  +  changeset:   a4f92ed23982be056b9852de5dfe873eaac7f0de
> >>  +  revision:    1
> >>  +  user:        test
> >>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> >>  +  summary:     Added tag 1.0 for changeset 2ef0ac749a14
> >>  +  branch:      default
> >>  +
> >>  +
> >>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(,2)^&style=raw'
> >>  +  200 Script output follows
> >>  +
> >>  +
> >>  +  # HG changesets search
> >>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> >>  +  # Query "last(all(,2)^"
> >>  +
> >>  +
> >>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(al(),2)^&style=raw'
> >>  +  200 Script output follows
> >>  +
> >>  +
> >>  +  # HG changesets search
> >>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> >>  +  # Query "last(al(),2)^"
> >>  +
> >>  +
> >>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(anotherthing)&style=raw'
> >>  +  200 Script output follows
> >>  +
> >>  +
> >>  +  # HG changesets search
> >>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> >>  +  # Query "bookmark(anotherthing)"
> >>  +
> >>  +  changeset:   2ef0ac749a14e4f57a5a822464a0902c6f7f448f
> >>  +  revision:    0
> >>  +  user:        test
> >>  +  date:        Thu, 01 Jan 1970 00:00:00 +0000
> >>  +  summary:     base
> >>  +  tag:         1.0
> >>  +  bookmark:    anotherthing
> >>  +
> >>  +
> >>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(abc)&style=raw'
> >>  +  200 Script output follows
> >>  +
> >>  +
> >>  +  # HG changesets search
> >>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> >>  +  # Query "bookmark(abc)"
> >>  +
> >>  +
> >>  +  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=deadbeef:&style=raw'
> >>  +  200 Script output follows
> >>  +
> >>  +
> >>  +  # HG changesets search
> >>  +  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
> >>  +  # Query "deadbeef:"
> >>  +
> >>  +
> >>  +
> >>   File-related
> >>
> >>     $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/foo/?style=raw'
> >>  _______________________________________________
> >>  Mercurial-devel mailing list
> >>  Mercurial-devel@selenic.com
> >>  http://selenic.com/mailman/listinfo/mercurial-devel

Patch

diff -r f32bf1774cc3 -r c9ded0751566 mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py	Fri Aug 09 22:52:58 2013 +0400
+++ b/mercurial/hgweb/webcommands.py	Wed Aug 07 01:16:14 2013 +0400
@@ -16,6 +16,8 @@ 
 from mercurial import help as helpmod
 from mercurial import scmutil
 from mercurial.i18n import _
+from mercurial.error import ParseError, RepoLookupError, Abort
+from mercurial import parser, revset
 
 # __all__ is populated with the allowed commands. Be sure to add to it if
 # you're adding a new command, or the new command won't work.
@@ -141,18 +143,50 @@ 
 
             yield ctx
 
+    def revsetsearch(revdef):
+        revs = revset.match(web.repo.ui, revdef)(web.repo, list(web.repo))
+        for r in revs:
+            yield web.repo[r]
+
     searchfuncs = {
         'rev': revsearch,
         'kw': keywordsearch,
+        'revset': revsetsearch,
     }
 
     def getsearchmode(query):
         try:
             ctx = web.repo[query]
         except (error.RepoError, error.LookupError):
+            # query is not an exact revision pointer, need to
+            # decide if it's a revset expession or keywords
+            pass
+        else:
+            return 'rev', ctx
+
+        revdef = 'reverse(%s)' % query
+        try:
+            p = parser.parser(revset.tokenize, revset.elements)
+            tree, pos = p.parse(revdef)
+        except ParseError:
+            # can't parse to a revset tree
+            return 'kw', query
+
+        if revset.depth(tree) <= 2:
+            # no revset syntax used
+            return 'kw', query
+
+        mfunc = revset.match(None, revdef)
+        try:
+            # try running against empty subset
+            mfunc(web.repo, [])
+            # ParseError: wrongly placed tokens, wrongs arguments, etc
+            # RepoLookupError: no such revision, e.g. in 'revision:'
+            # Abort: bookmark/tag not exists
+        except (ParseError, RepoLookupError, Abort):
             return 'kw', query
         else:
-            return 'rev', ctx
+            return 'revset', revdef
 
     def changelist(**map):
         count = 0
diff -r f32bf1774cc3 -r c9ded0751566 tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t	Fri Aug 09 22:52:58 2013 +0400
+++ b/tests/test-hgweb-commands.t	Wed Aug 07 01:16:14 2013 +0400
@@ -537,6 +537,101 @@ 
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=stable&style=raw' | grep 'revision:'
   revision:    2
 
+Search with revset syntax
+
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=tip^&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "tip^"
+  
+  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
+  revision:    2
+  user:        test
+  date:        Thu, 01 Jan 1970 00:00:00 +0000
+  summary:     branch
+  branch:      stable
+  
+  
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(),2)^&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "last(all(),2)^"
+  
+  changeset:   1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
+  revision:    2
+  user:        test
+  date:        Thu, 01 Jan 1970 00:00:00 +0000
+  summary:     branch
+  branch:      stable
+  
+  changeset:   a4f92ed23982be056b9852de5dfe873eaac7f0de
+  revision:    1
+  user:        test
+  date:        Thu, 01 Jan 1970 00:00:00 +0000
+  summary:     Added tag 1.0 for changeset 2ef0ac749a14
+  branch:      default
+  
+  
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(all(,2)^&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "last(all(,2)^"
+  
+  
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=last(al(),2)^&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "last(al(),2)^"
+  
+  
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(anotherthing)&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "bookmark(anotherthing)"
+  
+  changeset:   2ef0ac749a14e4f57a5a822464a0902c6f7f448f
+  revision:    0
+  user:        test
+  date:        Thu, 01 Jan 1970 00:00:00 +0000
+  summary:     base
+  tag:         1.0
+  bookmark:    anotherthing
+  
+  
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=bookmark(abc)&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "bookmark(abc)"
+  
+  
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=deadbeef:&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "deadbeef:"
+  
+  
+
 File-related
 
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/foo/?style=raw'