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.
--- 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'