changeset 19722:bf15935b68a3

hgweb: add revset syntax support to search This mode 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 - no revset regexes are used in the query (strings which start with 're:') - only functions explicitly marked as safe in revset.py are used in the query Add several new tests for different parsing conditions and exception handling.
author Alexander Plavin <alexander@plav.in>
date Fri, 06 Sep 2013 13:30:56 +0400
parents d8ca6d965230
children 7999f4fa155a
files mercurial/hgweb/webcommands.py tests/test-hgweb-commands.t
diffstat 2 files changed, 186 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/hgweb/webcommands.py	Fri Sep 06 13:30:56 2013 +0400
+++ b/mercurial/hgweb/webcommands.py	Fri Sep 06 13:30:56 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 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.
@@ -111,6 +113,7 @@
 def _search(web, req, tmpl):
     MODE_REVISION = 'rev'
     MODE_KEYWORD = 'keyword'
+    MODE_REVSET = 'revset'
 
     def revsearch(ctx):
         yield ctx
@@ -143,19 +146,56 @@
 
             yield ctx
 
+    def revsetsearch(revs):
+        for r in revs:
+            yield web.repo[r]
+
     searchfuncs = {
         MODE_REVISION: revsearch,
         MODE_KEYWORD: keywordsearch,
+        MODE_REVSET: revsetsearch,
     }
 
     def getsearchmode(query):
         try:
             ctx = web.repo[query]
         except (error.RepoError, error.LookupError):
-            return MODE_KEYWORD, query
+            # query is not an exact revision pointer, need to
+            # decide if it's a revset expession or keywords
+            pass
         else:
             return MODE_REVISION, ctx
 
+        revdef = 'reverse(%s)' % query
+        try:
+            tree, pos = revset.parse(revdef)
+        except ParseError:
+            # can't parse to a revset tree
+            return MODE_KEYWORD, query
+
+        if revset.depth(tree) <= 2:
+            # no revset syntax used
+            return MODE_KEYWORD, query
+
+        if util.any((token, (value or '')[:3]) == ('string', 're:')
+                    for token, value, pos in revset.tokenize(revdef)):
+            return MODE_KEYWORD, query
+
+        funcsused = revset.funcsused(tree)
+        if not funcsused.issubset(revset.safesymbols):
+            return MODE_KEYWORD, query
+
+        mfunc = revset.match(web.repo.ui, revdef)
+        try:
+            revs = mfunc(web.repo, list(web.repo))
+            return MODE_REVSET, revs
+            # ParseError: wrongly placed tokens, wrongs arguments, etc
+            # RepoLookupError: no such revision, e.g. in 'revision:'
+            # Abort: bookmark/tag not exists
+            # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
+        except (ParseError, RepoLookupError, Abort, LookupError):
+            return MODE_KEYWORD, query
+
     def changelist(**map):
         count = 0
 
--- a/tests/test-hgweb-commands.t	Fri Sep 06 13:30:56 2013 +0400
+++ b/tests/test-hgweb-commands.t	Fri Sep 06 13:30:56 2013 +0400
@@ -540,6 +540,151 @@
   $ "$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:"
+  
+  
+
+  $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=user("test")&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "user("test")"
+  
+  changeset:   cad8025a2e87f88c06259790adfa15acb4080123
+  revision:    3
+  user:        test
+  date:        Thu, 01 Jan 1970 00:00:00 +0000
+  summary:     branch commit with null character: \x00 (esc)
+  branch:      unstable
+  tag:         tip
+  bookmark:    something
+  
+  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
+  
+  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=user("re:test")&style=raw'
+  200 Script output follows
+  
+  
+  # HG changesets search
+  # Node ID cad8025a2e87f88c06259790adfa15acb4080123
+  # Query "user("re:test")"
+  
+  
+
 File-related
 
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/foo/?style=raw'