Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hgweb/webcommands.py @ 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 | 145636d31bb4 |
children | 6907251122e3 |
comparison
equal
deleted
inserted
replaced
19721:d8ca6d965230 | 19722:bf15935b68a3 |
---|---|
14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND | 14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND |
15 from mercurial import graphmod, patch | 15 from mercurial import graphmod, patch |
16 from mercurial import help as helpmod | 16 from mercurial import help as helpmod |
17 from mercurial import scmutil | 17 from mercurial import scmutil |
18 from mercurial.i18n import _ | 18 from mercurial.i18n import _ |
19 from mercurial.error import ParseError, RepoLookupError, Abort | |
20 from mercurial import revset | |
19 | 21 |
20 # __all__ is populated with the allowed commands. Be sure to add to it if | 22 # __all__ is populated with the allowed commands. Be sure to add to it if |
21 # you're adding a new command, or the new command won't work. | 23 # you're adding a new command, or the new command won't work. |
22 | 24 |
23 __all__ = [ | 25 __all__ = [ |
109 raise inst | 111 raise inst |
110 | 112 |
111 def _search(web, req, tmpl): | 113 def _search(web, req, tmpl): |
112 MODE_REVISION = 'rev' | 114 MODE_REVISION = 'rev' |
113 MODE_KEYWORD = 'keyword' | 115 MODE_KEYWORD = 'keyword' |
116 MODE_REVSET = 'revset' | |
114 | 117 |
115 def revsearch(ctx): | 118 def revsearch(ctx): |
116 yield ctx | 119 yield ctx |
117 | 120 |
118 def keywordsearch(query): | 121 def keywordsearch(query): |
141 if miss: | 144 if miss: |
142 continue | 145 continue |
143 | 146 |
144 yield ctx | 147 yield ctx |
145 | 148 |
149 def revsetsearch(revs): | |
150 for r in revs: | |
151 yield web.repo[r] | |
152 | |
146 searchfuncs = { | 153 searchfuncs = { |
147 MODE_REVISION: revsearch, | 154 MODE_REVISION: revsearch, |
148 MODE_KEYWORD: keywordsearch, | 155 MODE_KEYWORD: keywordsearch, |
156 MODE_REVSET: revsetsearch, | |
149 } | 157 } |
150 | 158 |
151 def getsearchmode(query): | 159 def getsearchmode(query): |
152 try: | 160 try: |
153 ctx = web.repo[query] | 161 ctx = web.repo[query] |
154 except (error.RepoError, error.LookupError): | 162 except (error.RepoError, error.LookupError): |
155 return MODE_KEYWORD, query | 163 # query is not an exact revision pointer, need to |
164 # decide if it's a revset expession or keywords | |
165 pass | |
156 else: | 166 else: |
157 return MODE_REVISION, ctx | 167 return MODE_REVISION, ctx |
168 | |
169 revdef = 'reverse(%s)' % query | |
170 try: | |
171 tree, pos = revset.parse(revdef) | |
172 except ParseError: | |
173 # can't parse to a revset tree | |
174 return MODE_KEYWORD, query | |
175 | |
176 if revset.depth(tree) <= 2: | |
177 # no revset syntax used | |
178 return MODE_KEYWORD, query | |
179 | |
180 if util.any((token, (value or '')[:3]) == ('string', 're:') | |
181 for token, value, pos in revset.tokenize(revdef)): | |
182 return MODE_KEYWORD, query | |
183 | |
184 funcsused = revset.funcsused(tree) | |
185 if not funcsused.issubset(revset.safesymbols): | |
186 return MODE_KEYWORD, query | |
187 | |
188 mfunc = revset.match(web.repo.ui, revdef) | |
189 try: | |
190 revs = mfunc(web.repo, list(web.repo)) | |
191 return MODE_REVSET, revs | |
192 # ParseError: wrongly placed tokens, wrongs arguments, etc | |
193 # RepoLookupError: no such revision, e.g. in 'revision:' | |
194 # Abort: bookmark/tag not exists | |
195 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo | |
196 except (ParseError, RepoLookupError, Abort, LookupError): | |
197 return MODE_KEYWORD, query | |
158 | 198 |
159 def changelist(**map): | 199 def changelist(**map): |
160 count = 0 | 200 count = 0 |
161 | 201 |
162 for ctx in searchfunc(funcarg): | 202 for ctx in searchfunc(funcarg): |