##// END OF EJS Templates
hgweb: remove unused argument of entries function in filelog...
Alexander Plavin -
r20021:4830763f default
parent child Browse files
Show More
@@ -1,1097 +1,1097
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import os, mimetypes, re, cgi, copy
8 import os, mimetypes, re, cgi, copy
9 import webutil
9 import webutil
10 from mercurial import error, encoding, archival, templater, templatefilters
10 from mercurial import error, encoding, archival, templater, templatefilters
11 from mercurial.node import short, hex, nullid
11 from mercurial.node import short, hex, nullid
12 from mercurial import util
12 from mercurial import util
13 from common import paritygen, staticfile, get_contact, ErrorResponse
13 from common import paritygen, staticfile, get_contact, ErrorResponse
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
19 from mercurial.error import ParseError, RepoLookupError, Abort
20 from mercurial import revset
20 from mercurial import revset
21
21
22 # __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
23 # 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.
24
24
25 __all__ = [
25 __all__ = [
26 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
26 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
27 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
27 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
28 'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
28 'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
29 ]
29 ]
30
30
31 def log(web, req, tmpl):
31 def log(web, req, tmpl):
32 if 'file' in req.form and req.form['file'][0]:
32 if 'file' in req.form and req.form['file'][0]:
33 return filelog(web, req, tmpl)
33 return filelog(web, req, tmpl)
34 else:
34 else:
35 return changelog(web, req, tmpl)
35 return changelog(web, req, tmpl)
36
36
37 def rawfile(web, req, tmpl):
37 def rawfile(web, req, tmpl):
38 guessmime = web.configbool('web', 'guessmime', False)
38 guessmime = web.configbool('web', 'guessmime', False)
39
39
40 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
40 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
41 if not path:
41 if not path:
42 content = manifest(web, req, tmpl)
42 content = manifest(web, req, tmpl)
43 req.respond(HTTP_OK, web.ctype)
43 req.respond(HTTP_OK, web.ctype)
44 return content
44 return content
45
45
46 try:
46 try:
47 fctx = webutil.filectx(web.repo, req)
47 fctx = webutil.filectx(web.repo, req)
48 except error.LookupError, inst:
48 except error.LookupError, inst:
49 try:
49 try:
50 content = manifest(web, req, tmpl)
50 content = manifest(web, req, tmpl)
51 req.respond(HTTP_OK, web.ctype)
51 req.respond(HTTP_OK, web.ctype)
52 return content
52 return content
53 except ErrorResponse:
53 except ErrorResponse:
54 raise inst
54 raise inst
55
55
56 path = fctx.path()
56 path = fctx.path()
57 text = fctx.data()
57 text = fctx.data()
58 mt = 'application/binary'
58 mt = 'application/binary'
59 if guessmime:
59 if guessmime:
60 mt = mimetypes.guess_type(path)[0]
60 mt = mimetypes.guess_type(path)[0]
61 if mt is None:
61 if mt is None:
62 mt = util.binary(text) and 'application/binary' or 'text/plain'
62 mt = util.binary(text) and 'application/binary' or 'text/plain'
63 if mt.startswith('text/'):
63 if mt.startswith('text/'):
64 mt += '; charset="%s"' % encoding.encoding
64 mt += '; charset="%s"' % encoding.encoding
65
65
66 req.respond(HTTP_OK, mt, path, body=text)
66 req.respond(HTTP_OK, mt, path, body=text)
67 return []
67 return []
68
68
69 def _filerevision(web, tmpl, fctx):
69 def _filerevision(web, tmpl, fctx):
70 f = fctx.path()
70 f = fctx.path()
71 text = fctx.data()
71 text = fctx.data()
72 parity = paritygen(web.stripecount)
72 parity = paritygen(web.stripecount)
73
73
74 if util.binary(text):
74 if util.binary(text):
75 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
75 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
76 text = '(binary:%s)' % mt
76 text = '(binary:%s)' % mt
77
77
78 def lines():
78 def lines():
79 for lineno, t in enumerate(text.splitlines(True)):
79 for lineno, t in enumerate(text.splitlines(True)):
80 yield {"line": t,
80 yield {"line": t,
81 "lineid": "l%d" % (lineno + 1),
81 "lineid": "l%d" % (lineno + 1),
82 "linenumber": "% 6d" % (lineno + 1),
82 "linenumber": "% 6d" % (lineno + 1),
83 "parity": parity.next()}
83 "parity": parity.next()}
84
84
85 return tmpl("filerevision",
85 return tmpl("filerevision",
86 file=f,
86 file=f,
87 path=webutil.up(f),
87 path=webutil.up(f),
88 text=lines(),
88 text=lines(),
89 rev=fctx.rev(),
89 rev=fctx.rev(),
90 node=fctx.hex(),
90 node=fctx.hex(),
91 author=fctx.user(),
91 author=fctx.user(),
92 date=fctx.date(),
92 date=fctx.date(),
93 desc=fctx.description(),
93 desc=fctx.description(),
94 extra=fctx.extra(),
94 extra=fctx.extra(),
95 branch=webutil.nodebranchnodefault(fctx),
95 branch=webutil.nodebranchnodefault(fctx),
96 parent=webutil.parents(fctx),
96 parent=webutil.parents(fctx),
97 child=webutil.children(fctx),
97 child=webutil.children(fctx),
98 rename=webutil.renamelink(fctx),
98 rename=webutil.renamelink(fctx),
99 permissions=fctx.manifest().flags(f))
99 permissions=fctx.manifest().flags(f))
100
100
101 def file(web, req, tmpl):
101 def file(web, req, tmpl):
102 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
102 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
103 if not path:
103 if not path:
104 return manifest(web, req, tmpl)
104 return manifest(web, req, tmpl)
105 try:
105 try:
106 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
106 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
107 except error.LookupError, inst:
107 except error.LookupError, inst:
108 try:
108 try:
109 return manifest(web, req, tmpl)
109 return manifest(web, req, tmpl)
110 except ErrorResponse:
110 except ErrorResponse:
111 raise inst
111 raise inst
112
112
113 def _search(web, req, tmpl):
113 def _search(web, req, tmpl):
114 MODE_REVISION = 'rev'
114 MODE_REVISION = 'rev'
115 MODE_KEYWORD = 'keyword'
115 MODE_KEYWORD = 'keyword'
116 MODE_REVSET = 'revset'
116 MODE_REVSET = 'revset'
117
117
118 def revsearch(ctx):
118 def revsearch(ctx):
119 yield ctx
119 yield ctx
120
120
121 def keywordsearch(query):
121 def keywordsearch(query):
122 lower = encoding.lower
122 lower = encoding.lower
123 qw = lower(query).split()
123 qw = lower(query).split()
124
124
125 def revgen():
125 def revgen():
126 cl = web.repo.changelog
126 cl = web.repo.changelog
127 for i in xrange(len(web.repo) - 1, 0, -100):
127 for i in xrange(len(web.repo) - 1, 0, -100):
128 l = []
128 l = []
129 for j in cl.revs(max(0, i - 99), i):
129 for j in cl.revs(max(0, i - 99), i):
130 ctx = web.repo[j]
130 ctx = web.repo[j]
131 l.append(ctx)
131 l.append(ctx)
132 l.reverse()
132 l.reverse()
133 for e in l:
133 for e in l:
134 yield e
134 yield e
135
135
136 for ctx in revgen():
136 for ctx in revgen():
137 miss = 0
137 miss = 0
138 for q in qw:
138 for q in qw:
139 if not (q in lower(ctx.user()) or
139 if not (q in lower(ctx.user()) or
140 q in lower(ctx.description()) or
140 q in lower(ctx.description()) or
141 q in lower(" ".join(ctx.files()))):
141 q in lower(" ".join(ctx.files()))):
142 miss = 1
142 miss = 1
143 break
143 break
144 if miss:
144 if miss:
145 continue
145 continue
146
146
147 yield ctx
147 yield ctx
148
148
149 def revsetsearch(revs):
149 def revsetsearch(revs):
150 for r in revs:
150 for r in revs:
151 yield web.repo[r]
151 yield web.repo[r]
152
152
153 searchfuncs = {
153 searchfuncs = {
154 MODE_REVISION: (revsearch, 'exact revision search'),
154 MODE_REVISION: (revsearch, 'exact revision search'),
155 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
155 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
156 MODE_REVSET: (revsetsearch, 'revset expression search'),
156 MODE_REVSET: (revsetsearch, 'revset expression search'),
157 }
157 }
158
158
159 def getsearchmode(query):
159 def getsearchmode(query):
160 try:
160 try:
161 ctx = web.repo[query]
161 ctx = web.repo[query]
162 except (error.RepoError, error.LookupError):
162 except (error.RepoError, error.LookupError):
163 # query is not an exact revision pointer, need to
163 # query is not an exact revision pointer, need to
164 # decide if it's a revset expression or keywords
164 # decide if it's a revset expression or keywords
165 pass
165 pass
166 else:
166 else:
167 return MODE_REVISION, ctx
167 return MODE_REVISION, ctx
168
168
169 revdef = 'reverse(%s)' % query
169 revdef = 'reverse(%s)' % query
170 try:
170 try:
171 tree, pos = revset.parse(revdef)
171 tree, pos = revset.parse(revdef)
172 except ParseError:
172 except ParseError:
173 # can't parse to a revset tree
173 # can't parse to a revset tree
174 return MODE_KEYWORD, query
174 return MODE_KEYWORD, query
175
175
176 if revset.depth(tree) <= 2:
176 if revset.depth(tree) <= 2:
177 # no revset syntax used
177 # no revset syntax used
178 return MODE_KEYWORD, query
178 return MODE_KEYWORD, query
179
179
180 if util.any((token, (value or '')[:3]) == ('string', 're:')
180 if util.any((token, (value or '')[:3]) == ('string', 're:')
181 for token, value, pos in revset.tokenize(revdef)):
181 for token, value, pos in revset.tokenize(revdef)):
182 return MODE_KEYWORD, query
182 return MODE_KEYWORD, query
183
183
184 funcsused = revset.funcsused(tree)
184 funcsused = revset.funcsused(tree)
185 if not funcsused.issubset(revset.safesymbols):
185 if not funcsused.issubset(revset.safesymbols):
186 return MODE_KEYWORD, query
186 return MODE_KEYWORD, query
187
187
188 mfunc = revset.match(web.repo.ui, revdef)
188 mfunc = revset.match(web.repo.ui, revdef)
189 try:
189 try:
190 revs = mfunc(web.repo, list(web.repo))
190 revs = mfunc(web.repo, list(web.repo))
191 return MODE_REVSET, revs
191 return MODE_REVSET, revs
192 # ParseError: wrongly placed tokens, wrongs arguments, etc
192 # ParseError: wrongly placed tokens, wrongs arguments, etc
193 # RepoLookupError: no such revision, e.g. in 'revision:'
193 # RepoLookupError: no such revision, e.g. in 'revision:'
194 # Abort: bookmark/tag not exists
194 # Abort: bookmark/tag not exists
195 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
195 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
196 except (ParseError, RepoLookupError, Abort, LookupError):
196 except (ParseError, RepoLookupError, Abort, LookupError):
197 return MODE_KEYWORD, query
197 return MODE_KEYWORD, query
198
198
199 def changelist(**map):
199 def changelist(**map):
200 count = 0
200 count = 0
201
201
202 for ctx in searchfunc[0](funcarg):
202 for ctx in searchfunc[0](funcarg):
203 count += 1
203 count += 1
204 n = ctx.node()
204 n = ctx.node()
205 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
205 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
206 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
206 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
207
207
208 yield tmpl('searchentry',
208 yield tmpl('searchentry',
209 parity=parity.next(),
209 parity=parity.next(),
210 author=ctx.user(),
210 author=ctx.user(),
211 parent=webutil.parents(ctx),
211 parent=webutil.parents(ctx),
212 child=webutil.children(ctx),
212 child=webutil.children(ctx),
213 changelogtag=showtags,
213 changelogtag=showtags,
214 desc=ctx.description(),
214 desc=ctx.description(),
215 extra=ctx.extra(),
215 extra=ctx.extra(),
216 date=ctx.date(),
216 date=ctx.date(),
217 files=files,
217 files=files,
218 rev=ctx.rev(),
218 rev=ctx.rev(),
219 node=hex(n),
219 node=hex(n),
220 tags=webutil.nodetagsdict(web.repo, n),
220 tags=webutil.nodetagsdict(web.repo, n),
221 bookmarks=webutil.nodebookmarksdict(web.repo, n),
221 bookmarks=webutil.nodebookmarksdict(web.repo, n),
222 inbranch=webutil.nodeinbranch(web.repo, ctx),
222 inbranch=webutil.nodeinbranch(web.repo, ctx),
223 branches=webutil.nodebranchdict(web.repo, ctx))
223 branches=webutil.nodebranchdict(web.repo, ctx))
224
224
225 if count >= revcount:
225 if count >= revcount:
226 break
226 break
227
227
228 query = req.form['rev'][0]
228 query = req.form['rev'][0]
229 revcount = web.maxchanges
229 revcount = web.maxchanges
230 if 'revcount' in req.form:
230 if 'revcount' in req.form:
231 revcount = int(req.form.get('revcount', [revcount])[0])
231 revcount = int(req.form.get('revcount', [revcount])[0])
232 revcount = max(revcount, 1)
232 revcount = max(revcount, 1)
233 tmpl.defaults['sessionvars']['revcount'] = revcount
233 tmpl.defaults['sessionvars']['revcount'] = revcount
234
234
235 lessvars = copy.copy(tmpl.defaults['sessionvars'])
235 lessvars = copy.copy(tmpl.defaults['sessionvars'])
236 lessvars['revcount'] = max(revcount / 2, 1)
236 lessvars['revcount'] = max(revcount / 2, 1)
237 lessvars['rev'] = query
237 lessvars['rev'] = query
238 morevars = copy.copy(tmpl.defaults['sessionvars'])
238 morevars = copy.copy(tmpl.defaults['sessionvars'])
239 morevars['revcount'] = revcount * 2
239 morevars['revcount'] = revcount * 2
240 morevars['rev'] = query
240 morevars['rev'] = query
241
241
242 mode, funcarg = getsearchmode(query)
242 mode, funcarg = getsearchmode(query)
243
243
244 if 'forcekw' in req.form:
244 if 'forcekw' in req.form:
245 showforcekw = ''
245 showforcekw = ''
246 showunforcekw = searchfuncs[mode][1]
246 showunforcekw = searchfuncs[mode][1]
247 mode = MODE_KEYWORD
247 mode = MODE_KEYWORD
248 funcarg = query
248 funcarg = query
249 else:
249 else:
250 if mode != MODE_KEYWORD:
250 if mode != MODE_KEYWORD:
251 showforcekw = searchfuncs[MODE_KEYWORD][1]
251 showforcekw = searchfuncs[MODE_KEYWORD][1]
252 else:
252 else:
253 showforcekw = ''
253 showforcekw = ''
254 showunforcekw = ''
254 showunforcekw = ''
255
255
256 searchfunc = searchfuncs[mode]
256 searchfunc = searchfuncs[mode]
257
257
258 tip = web.repo['tip']
258 tip = web.repo['tip']
259 parity = paritygen(web.stripecount)
259 parity = paritygen(web.stripecount)
260
260
261 return tmpl('search', query=query, node=tip.hex(),
261 return tmpl('search', query=query, node=tip.hex(),
262 entries=changelist, archives=web.archivelist("tip"),
262 entries=changelist, archives=web.archivelist("tip"),
263 morevars=morevars, lessvars=lessvars,
263 morevars=morevars, lessvars=lessvars,
264 modedesc=searchfunc[1],
264 modedesc=searchfunc[1],
265 showforcekw=showforcekw, showunforcekw=showunforcekw)
265 showforcekw=showforcekw, showunforcekw=showunforcekw)
266
266
267 def changelog(web, req, tmpl, shortlog=False):
267 def changelog(web, req, tmpl, shortlog=False):
268
268
269 query = ''
269 query = ''
270 if 'node' in req.form:
270 if 'node' in req.form:
271 ctx = webutil.changectx(web.repo, req)
271 ctx = webutil.changectx(web.repo, req)
272 elif 'rev' in req.form:
272 elif 'rev' in req.form:
273 return _search(web, req, tmpl)
273 return _search(web, req, tmpl)
274 else:
274 else:
275 ctx = web.repo['tip']
275 ctx = web.repo['tip']
276
276
277 def changelist():
277 def changelist():
278 revs = []
278 revs = []
279 if pos != -1:
279 if pos != -1:
280 revs = web.repo.changelog.revs(pos, 0)
280 revs = web.repo.changelog.revs(pos, 0)
281 curcount = 0
281 curcount = 0
282 for i in revs:
282 for i in revs:
283 ctx = web.repo[i]
283 ctx = web.repo[i]
284 n = ctx.node()
284 n = ctx.node()
285 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
285 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
286 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
286 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
287
287
288 curcount += 1
288 curcount += 1
289 if curcount > revcount + 1:
289 if curcount > revcount + 1:
290 break
290 break
291 yield {"parity": parity.next(),
291 yield {"parity": parity.next(),
292 "author": ctx.user(),
292 "author": ctx.user(),
293 "parent": webutil.parents(ctx, i - 1),
293 "parent": webutil.parents(ctx, i - 1),
294 "child": webutil.children(ctx, i + 1),
294 "child": webutil.children(ctx, i + 1),
295 "changelogtag": showtags,
295 "changelogtag": showtags,
296 "desc": ctx.description(),
296 "desc": ctx.description(),
297 "extra": ctx.extra(),
297 "extra": ctx.extra(),
298 "date": ctx.date(),
298 "date": ctx.date(),
299 "files": files,
299 "files": files,
300 "rev": i,
300 "rev": i,
301 "node": hex(n),
301 "node": hex(n),
302 "tags": webutil.nodetagsdict(web.repo, n),
302 "tags": webutil.nodetagsdict(web.repo, n),
303 "bookmarks": webutil.nodebookmarksdict(web.repo, n),
303 "bookmarks": webutil.nodebookmarksdict(web.repo, n),
304 "inbranch": webutil.nodeinbranch(web.repo, ctx),
304 "inbranch": webutil.nodeinbranch(web.repo, ctx),
305 "branches": webutil.nodebranchdict(web.repo, ctx)
305 "branches": webutil.nodebranchdict(web.repo, ctx)
306 }
306 }
307
307
308 revcount = shortlog and web.maxshortchanges or web.maxchanges
308 revcount = shortlog and web.maxshortchanges or web.maxchanges
309 if 'revcount' in req.form:
309 if 'revcount' in req.form:
310 revcount = int(req.form.get('revcount', [revcount])[0])
310 revcount = int(req.form.get('revcount', [revcount])[0])
311 revcount = max(revcount, 1)
311 revcount = max(revcount, 1)
312 tmpl.defaults['sessionvars']['revcount'] = revcount
312 tmpl.defaults['sessionvars']['revcount'] = revcount
313
313
314 lessvars = copy.copy(tmpl.defaults['sessionvars'])
314 lessvars = copy.copy(tmpl.defaults['sessionvars'])
315 lessvars['revcount'] = max(revcount / 2, 1)
315 lessvars['revcount'] = max(revcount / 2, 1)
316 morevars = copy.copy(tmpl.defaults['sessionvars'])
316 morevars = copy.copy(tmpl.defaults['sessionvars'])
317 morevars['revcount'] = revcount * 2
317 morevars['revcount'] = revcount * 2
318
318
319 count = len(web.repo)
319 count = len(web.repo)
320 pos = ctx.rev()
320 pos = ctx.rev()
321 parity = paritygen(web.stripecount)
321 parity = paritygen(web.stripecount)
322
322
323 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
323 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
324
324
325 entries = list(changelist())
325 entries = list(changelist())
326 latestentry = entries[:1]
326 latestentry = entries[:1]
327 if len(entries) > revcount:
327 if len(entries) > revcount:
328 nextentry = entries[-1:]
328 nextentry = entries[-1:]
329 entries = entries[:-1]
329 entries = entries[:-1]
330 else:
330 else:
331 nextentry = []
331 nextentry = []
332
332
333 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
333 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
334 node=ctx.hex(), rev=pos, changesets=count,
334 node=ctx.hex(), rev=pos, changesets=count,
335 entries=entries,
335 entries=entries,
336 latestentry=latestentry, nextentry=nextentry,
336 latestentry=latestentry, nextentry=nextentry,
337 archives=web.archivelist("tip"), revcount=revcount,
337 archives=web.archivelist("tip"), revcount=revcount,
338 morevars=morevars, lessvars=lessvars, query=query)
338 morevars=morevars, lessvars=lessvars, query=query)
339
339
340 def shortlog(web, req, tmpl):
340 def shortlog(web, req, tmpl):
341 return changelog(web, req, tmpl, shortlog=True)
341 return changelog(web, req, tmpl, shortlog=True)
342
342
343 def changeset(web, req, tmpl):
343 def changeset(web, req, tmpl):
344 ctx = webutil.changectx(web.repo, req)
344 ctx = webutil.changectx(web.repo, req)
345 basectx = webutil.basechangectx(web.repo, req)
345 basectx = webutil.basechangectx(web.repo, req)
346 if basectx is None:
346 if basectx is None:
347 basectx = ctx.p1()
347 basectx = ctx.p1()
348 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
348 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
349 showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
349 showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
350 ctx.node())
350 ctx.node())
351 showbranch = webutil.nodebranchnodefault(ctx)
351 showbranch = webutil.nodebranchnodefault(ctx)
352
352
353 files = []
353 files = []
354 parity = paritygen(web.stripecount)
354 parity = paritygen(web.stripecount)
355 for blockno, f in enumerate(ctx.files()):
355 for blockno, f in enumerate(ctx.files()):
356 template = f in ctx and 'filenodelink' or 'filenolink'
356 template = f in ctx and 'filenodelink' or 'filenolink'
357 files.append(tmpl(template,
357 files.append(tmpl(template,
358 node=ctx.hex(), file=f, blockno=blockno + 1,
358 node=ctx.hex(), file=f, blockno=blockno + 1,
359 parity=parity.next()))
359 parity=parity.next()))
360
360
361 style = web.config('web', 'style', 'paper')
361 style = web.config('web', 'style', 'paper')
362 if 'style' in req.form:
362 if 'style' in req.form:
363 style = req.form['style'][0]
363 style = req.form['style'][0]
364
364
365 parity = paritygen(web.stripecount)
365 parity = paritygen(web.stripecount)
366 diffs = webutil.diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
366 diffs = webutil.diffs(web.repo, tmpl, ctx, basectx, None, parity, style)
367
367
368 parity = paritygen(web.stripecount)
368 parity = paritygen(web.stripecount)
369 diffstatgen = webutil.diffstatgen(ctx, basectx)
369 diffstatgen = webutil.diffstatgen(ctx, basectx)
370 diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity)
370 diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity)
371
371
372 return tmpl('changeset',
372 return tmpl('changeset',
373 diff=diffs,
373 diff=diffs,
374 rev=ctx.rev(),
374 rev=ctx.rev(),
375 node=ctx.hex(),
375 node=ctx.hex(),
376 parent=webutil.parents(ctx),
376 parent=webutil.parents(ctx),
377 child=webutil.children(ctx),
377 child=webutil.children(ctx),
378 basenode=basectx.hex(),
378 basenode=basectx.hex(),
379 changesettag=showtags,
379 changesettag=showtags,
380 changesetbookmark=showbookmarks,
380 changesetbookmark=showbookmarks,
381 changesetbranch=showbranch,
381 changesetbranch=showbranch,
382 author=ctx.user(),
382 author=ctx.user(),
383 desc=ctx.description(),
383 desc=ctx.description(),
384 extra=ctx.extra(),
384 extra=ctx.extra(),
385 date=ctx.date(),
385 date=ctx.date(),
386 files=files,
386 files=files,
387 diffsummary=lambda **x: webutil.diffsummary(diffstatgen),
387 diffsummary=lambda **x: webutil.diffsummary(diffstatgen),
388 diffstat=diffstat,
388 diffstat=diffstat,
389 archives=web.archivelist(ctx.hex()),
389 archives=web.archivelist(ctx.hex()),
390 tags=webutil.nodetagsdict(web.repo, ctx.node()),
390 tags=webutil.nodetagsdict(web.repo, ctx.node()),
391 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
391 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
392 branch=webutil.nodebranchnodefault(ctx),
392 branch=webutil.nodebranchnodefault(ctx),
393 inbranch=webutil.nodeinbranch(web.repo, ctx),
393 inbranch=webutil.nodeinbranch(web.repo, ctx),
394 branches=webutil.nodebranchdict(web.repo, ctx))
394 branches=webutil.nodebranchdict(web.repo, ctx))
395
395
396 rev = changeset
396 rev = changeset
397
397
398 def decodepath(path):
398 def decodepath(path):
399 """Hook for mapping a path in the repository to a path in the
399 """Hook for mapping a path in the repository to a path in the
400 working copy.
400 working copy.
401
401
402 Extensions (e.g., largefiles) can override this to remap files in
402 Extensions (e.g., largefiles) can override this to remap files in
403 the virtual file system presented by the manifest command below."""
403 the virtual file system presented by the manifest command below."""
404 return path
404 return path
405
405
406 def manifest(web, req, tmpl):
406 def manifest(web, req, tmpl):
407 ctx = webutil.changectx(web.repo, req)
407 ctx = webutil.changectx(web.repo, req)
408 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
408 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
409 mf = ctx.manifest()
409 mf = ctx.manifest()
410 node = ctx.node()
410 node = ctx.node()
411
411
412 files = {}
412 files = {}
413 dirs = {}
413 dirs = {}
414 parity = paritygen(web.stripecount)
414 parity = paritygen(web.stripecount)
415
415
416 if path and path[-1] != "/":
416 if path and path[-1] != "/":
417 path += "/"
417 path += "/"
418 l = len(path)
418 l = len(path)
419 abspath = "/" + path
419 abspath = "/" + path
420
420
421 for full, n in mf.iteritems():
421 for full, n in mf.iteritems():
422 # the virtual path (working copy path) used for the full
422 # the virtual path (working copy path) used for the full
423 # (repository) path
423 # (repository) path
424 f = decodepath(full)
424 f = decodepath(full)
425
425
426 if f[:l] != path:
426 if f[:l] != path:
427 continue
427 continue
428 remain = f[l:]
428 remain = f[l:]
429 elements = remain.split('/')
429 elements = remain.split('/')
430 if len(elements) == 1:
430 if len(elements) == 1:
431 files[remain] = full
431 files[remain] = full
432 else:
432 else:
433 h = dirs # need to retain ref to dirs (root)
433 h = dirs # need to retain ref to dirs (root)
434 for elem in elements[0:-1]:
434 for elem in elements[0:-1]:
435 if elem not in h:
435 if elem not in h:
436 h[elem] = {}
436 h[elem] = {}
437 h = h[elem]
437 h = h[elem]
438 if len(h) > 1:
438 if len(h) > 1:
439 break
439 break
440 h[None] = None # denotes files present
440 h[None] = None # denotes files present
441
441
442 if mf and not files and not dirs:
442 if mf and not files and not dirs:
443 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
443 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
444
444
445 def filelist(**map):
445 def filelist(**map):
446 for f in sorted(files):
446 for f in sorted(files):
447 full = files[f]
447 full = files[f]
448
448
449 fctx = ctx.filectx(full)
449 fctx = ctx.filectx(full)
450 yield {"file": full,
450 yield {"file": full,
451 "parity": parity.next(),
451 "parity": parity.next(),
452 "basename": f,
452 "basename": f,
453 "date": fctx.date(),
453 "date": fctx.date(),
454 "size": fctx.size(),
454 "size": fctx.size(),
455 "permissions": mf.flags(full)}
455 "permissions": mf.flags(full)}
456
456
457 def dirlist(**map):
457 def dirlist(**map):
458 for d in sorted(dirs):
458 for d in sorted(dirs):
459
459
460 emptydirs = []
460 emptydirs = []
461 h = dirs[d]
461 h = dirs[d]
462 while isinstance(h, dict) and len(h) == 1:
462 while isinstance(h, dict) and len(h) == 1:
463 k, v = h.items()[0]
463 k, v = h.items()[0]
464 if v:
464 if v:
465 emptydirs.append(k)
465 emptydirs.append(k)
466 h = v
466 h = v
467
467
468 path = "%s%s" % (abspath, d)
468 path = "%s%s" % (abspath, d)
469 yield {"parity": parity.next(),
469 yield {"parity": parity.next(),
470 "path": path,
470 "path": path,
471 "emptydirs": "/".join(emptydirs),
471 "emptydirs": "/".join(emptydirs),
472 "basename": d}
472 "basename": d}
473
473
474 return tmpl("manifest",
474 return tmpl("manifest",
475 rev=ctx.rev(),
475 rev=ctx.rev(),
476 node=hex(node),
476 node=hex(node),
477 path=abspath,
477 path=abspath,
478 up=webutil.up(abspath),
478 up=webutil.up(abspath),
479 upparity=parity.next(),
479 upparity=parity.next(),
480 fentries=filelist,
480 fentries=filelist,
481 dentries=dirlist,
481 dentries=dirlist,
482 archives=web.archivelist(hex(node)),
482 archives=web.archivelist(hex(node)),
483 tags=webutil.nodetagsdict(web.repo, node),
483 tags=webutil.nodetagsdict(web.repo, node),
484 bookmarks=webutil.nodebookmarksdict(web.repo, node),
484 bookmarks=webutil.nodebookmarksdict(web.repo, node),
485 inbranch=webutil.nodeinbranch(web.repo, ctx),
485 inbranch=webutil.nodeinbranch(web.repo, ctx),
486 branches=webutil.nodebranchdict(web.repo, ctx))
486 branches=webutil.nodebranchdict(web.repo, ctx))
487
487
488 def tags(web, req, tmpl):
488 def tags(web, req, tmpl):
489 i = list(reversed(web.repo.tagslist()))
489 i = list(reversed(web.repo.tagslist()))
490 parity = paritygen(web.stripecount)
490 parity = paritygen(web.stripecount)
491
491
492 def entries(notip, latestonly, **map):
492 def entries(notip, latestonly, **map):
493 t = i
493 t = i
494 if notip:
494 if notip:
495 t = [(k, n) for k, n in i if k != "tip"]
495 t = [(k, n) for k, n in i if k != "tip"]
496 if latestonly:
496 if latestonly:
497 t = t[:1]
497 t = t[:1]
498 for k, n in t:
498 for k, n in t:
499 yield {"parity": parity.next(),
499 yield {"parity": parity.next(),
500 "tag": k,
500 "tag": k,
501 "date": web.repo[n].date(),
501 "date": web.repo[n].date(),
502 "node": hex(n)}
502 "node": hex(n)}
503
503
504 return tmpl("tags",
504 return tmpl("tags",
505 node=hex(web.repo.changelog.tip()),
505 node=hex(web.repo.changelog.tip()),
506 entries=lambda **x: entries(False, False, **x),
506 entries=lambda **x: entries(False, False, **x),
507 entriesnotip=lambda **x: entries(True, False, **x),
507 entriesnotip=lambda **x: entries(True, False, **x),
508 latestentry=lambda **x: entries(True, True, **x))
508 latestentry=lambda **x: entries(True, True, **x))
509
509
510 def bookmarks(web, req, tmpl):
510 def bookmarks(web, req, tmpl):
511 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
511 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
512 parity = paritygen(web.stripecount)
512 parity = paritygen(web.stripecount)
513
513
514 def entries(latestonly, **map):
514 def entries(latestonly, **map):
515 if latestonly:
515 if latestonly:
516 t = [min(i)]
516 t = [min(i)]
517 else:
517 else:
518 t = sorted(i)
518 t = sorted(i)
519 for k, n in t:
519 for k, n in t:
520 yield {"parity": parity.next(),
520 yield {"parity": parity.next(),
521 "bookmark": k,
521 "bookmark": k,
522 "date": web.repo[n].date(),
522 "date": web.repo[n].date(),
523 "node": hex(n)}
523 "node": hex(n)}
524
524
525 return tmpl("bookmarks",
525 return tmpl("bookmarks",
526 node=hex(web.repo.changelog.tip()),
526 node=hex(web.repo.changelog.tip()),
527 entries=lambda **x: entries(latestonly=False, **x),
527 entries=lambda **x: entries(latestonly=False, **x),
528 latestentry=lambda **x: entries(latestonly=True, **x))
528 latestentry=lambda **x: entries(latestonly=True, **x))
529
529
530 def branches(web, req, tmpl):
530 def branches(web, req, tmpl):
531 tips = []
531 tips = []
532 heads = web.repo.heads()
532 heads = web.repo.heads()
533 parity = paritygen(web.stripecount)
533 parity = paritygen(web.stripecount)
534 sortkey = lambda ctx: (not ctx.closesbranch(), ctx.rev())
534 sortkey = lambda ctx: (not ctx.closesbranch(), ctx.rev())
535
535
536 def entries(limit, **map):
536 def entries(limit, **map):
537 count = 0
537 count = 0
538 if not tips:
538 if not tips:
539 for t, n in web.repo.branchtags().iteritems():
539 for t, n in web.repo.branchtags().iteritems():
540 tips.append(web.repo[n])
540 tips.append(web.repo[n])
541 for ctx in sorted(tips, key=sortkey, reverse=True):
541 for ctx in sorted(tips, key=sortkey, reverse=True):
542 if limit > 0 and count >= limit:
542 if limit > 0 and count >= limit:
543 return
543 return
544 count += 1
544 count += 1
545 if not web.repo.branchheads(ctx.branch()):
545 if not web.repo.branchheads(ctx.branch()):
546 status = 'closed'
546 status = 'closed'
547 elif ctx.node() not in heads:
547 elif ctx.node() not in heads:
548 status = 'inactive'
548 status = 'inactive'
549 else:
549 else:
550 status = 'open'
550 status = 'open'
551 yield {'parity': parity.next(),
551 yield {'parity': parity.next(),
552 'branch': ctx.branch(),
552 'branch': ctx.branch(),
553 'status': status,
553 'status': status,
554 'node': ctx.hex(),
554 'node': ctx.hex(),
555 'date': ctx.date()}
555 'date': ctx.date()}
556
556
557 return tmpl('branches', node=hex(web.repo.changelog.tip()),
557 return tmpl('branches', node=hex(web.repo.changelog.tip()),
558 entries=lambda **x: entries(0, **x),
558 entries=lambda **x: entries(0, **x),
559 latestentry=lambda **x: entries(1, **x))
559 latestentry=lambda **x: entries(1, **x))
560
560
561 def summary(web, req, tmpl):
561 def summary(web, req, tmpl):
562 i = reversed(web.repo.tagslist())
562 i = reversed(web.repo.tagslist())
563
563
564 def tagentries(**map):
564 def tagentries(**map):
565 parity = paritygen(web.stripecount)
565 parity = paritygen(web.stripecount)
566 count = 0
566 count = 0
567 for k, n in i:
567 for k, n in i:
568 if k == "tip": # skip tip
568 if k == "tip": # skip tip
569 continue
569 continue
570
570
571 count += 1
571 count += 1
572 if count > 10: # limit to 10 tags
572 if count > 10: # limit to 10 tags
573 break
573 break
574
574
575 yield tmpl("tagentry",
575 yield tmpl("tagentry",
576 parity=parity.next(),
576 parity=parity.next(),
577 tag=k,
577 tag=k,
578 node=hex(n),
578 node=hex(n),
579 date=web.repo[n].date())
579 date=web.repo[n].date())
580
580
581 def bookmarks(**map):
581 def bookmarks(**map):
582 parity = paritygen(web.stripecount)
582 parity = paritygen(web.stripecount)
583 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
583 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
584 for k, n in sorted(marks)[:10]: # limit to 10 bookmarks
584 for k, n in sorted(marks)[:10]: # limit to 10 bookmarks
585 yield {'parity': parity.next(),
585 yield {'parity': parity.next(),
586 'bookmark': k,
586 'bookmark': k,
587 'date': web.repo[n].date(),
587 'date': web.repo[n].date(),
588 'node': hex(n)}
588 'node': hex(n)}
589
589
590 def branches(**map):
590 def branches(**map):
591 parity = paritygen(web.stripecount)
591 parity = paritygen(web.stripecount)
592
592
593 b = web.repo.branchtags()
593 b = web.repo.branchtags()
594 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
594 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
595 for r, n, t in sorted(l):
595 for r, n, t in sorted(l):
596 yield {'parity': parity.next(),
596 yield {'parity': parity.next(),
597 'branch': t,
597 'branch': t,
598 'node': hex(n),
598 'node': hex(n),
599 'date': web.repo[n].date()}
599 'date': web.repo[n].date()}
600
600
601 def changelist(**map):
601 def changelist(**map):
602 parity = paritygen(web.stripecount, offset=start - end)
602 parity = paritygen(web.stripecount, offset=start - end)
603 l = [] # build a list in forward order for efficiency
603 l = [] # build a list in forward order for efficiency
604 revs = []
604 revs = []
605 if start < end:
605 if start < end:
606 revs = web.repo.changelog.revs(start, end - 1)
606 revs = web.repo.changelog.revs(start, end - 1)
607 for i in revs:
607 for i in revs:
608 ctx = web.repo[i]
608 ctx = web.repo[i]
609 n = ctx.node()
609 n = ctx.node()
610 hn = hex(n)
610 hn = hex(n)
611
611
612 l.append(tmpl(
612 l.append(tmpl(
613 'shortlogentry',
613 'shortlogentry',
614 parity=parity.next(),
614 parity=parity.next(),
615 author=ctx.user(),
615 author=ctx.user(),
616 desc=ctx.description(),
616 desc=ctx.description(),
617 extra=ctx.extra(),
617 extra=ctx.extra(),
618 date=ctx.date(),
618 date=ctx.date(),
619 rev=i,
619 rev=i,
620 node=hn,
620 node=hn,
621 tags=webutil.nodetagsdict(web.repo, n),
621 tags=webutil.nodetagsdict(web.repo, n),
622 bookmarks=webutil.nodebookmarksdict(web.repo, n),
622 bookmarks=webutil.nodebookmarksdict(web.repo, n),
623 inbranch=webutil.nodeinbranch(web.repo, ctx),
623 inbranch=webutil.nodeinbranch(web.repo, ctx),
624 branches=webutil.nodebranchdict(web.repo, ctx)))
624 branches=webutil.nodebranchdict(web.repo, ctx)))
625
625
626 l.reverse()
626 l.reverse()
627 yield l
627 yield l
628
628
629 tip = web.repo['tip']
629 tip = web.repo['tip']
630 count = len(web.repo)
630 count = len(web.repo)
631 start = max(0, count - web.maxchanges)
631 start = max(0, count - web.maxchanges)
632 end = min(count, start + web.maxchanges)
632 end = min(count, start + web.maxchanges)
633
633
634 return tmpl("summary",
634 return tmpl("summary",
635 desc=web.config("web", "description", "unknown"),
635 desc=web.config("web", "description", "unknown"),
636 owner=get_contact(web.config) or "unknown",
636 owner=get_contact(web.config) or "unknown",
637 lastchange=tip.date(),
637 lastchange=tip.date(),
638 tags=tagentries,
638 tags=tagentries,
639 bookmarks=bookmarks,
639 bookmarks=bookmarks,
640 branches=branches,
640 branches=branches,
641 shortlog=changelist,
641 shortlog=changelist,
642 node=tip.hex(),
642 node=tip.hex(),
643 archives=web.archivelist("tip"))
643 archives=web.archivelist("tip"))
644
644
645 def filediff(web, req, tmpl):
645 def filediff(web, req, tmpl):
646 fctx, ctx = None, None
646 fctx, ctx = None, None
647 try:
647 try:
648 fctx = webutil.filectx(web.repo, req)
648 fctx = webutil.filectx(web.repo, req)
649 except LookupError:
649 except LookupError:
650 ctx = webutil.changectx(web.repo, req)
650 ctx = webutil.changectx(web.repo, req)
651 path = webutil.cleanpath(web.repo, req.form['file'][0])
651 path = webutil.cleanpath(web.repo, req.form['file'][0])
652 if path not in ctx.files():
652 if path not in ctx.files():
653 raise
653 raise
654
654
655 if fctx is not None:
655 if fctx is not None:
656 n = fctx.node()
656 n = fctx.node()
657 path = fctx.path()
657 path = fctx.path()
658 ctx = fctx.changectx()
658 ctx = fctx.changectx()
659 else:
659 else:
660 n = ctx.node()
660 n = ctx.node()
661 # path already defined in except clause
661 # path already defined in except clause
662
662
663 parity = paritygen(web.stripecount)
663 parity = paritygen(web.stripecount)
664 style = web.config('web', 'style', 'paper')
664 style = web.config('web', 'style', 'paper')
665 if 'style' in req.form:
665 if 'style' in req.form:
666 style = req.form['style'][0]
666 style = req.form['style'][0]
667
667
668 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
668 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
669 rename = fctx and webutil.renamelink(fctx) or []
669 rename = fctx and webutil.renamelink(fctx) or []
670 ctx = fctx and fctx or ctx
670 ctx = fctx and fctx or ctx
671 return tmpl("filediff",
671 return tmpl("filediff",
672 file=path,
672 file=path,
673 node=hex(n),
673 node=hex(n),
674 rev=ctx.rev(),
674 rev=ctx.rev(),
675 date=ctx.date(),
675 date=ctx.date(),
676 desc=ctx.description(),
676 desc=ctx.description(),
677 extra=ctx.extra(),
677 extra=ctx.extra(),
678 author=ctx.user(),
678 author=ctx.user(),
679 rename=rename,
679 rename=rename,
680 branch=webutil.nodebranchnodefault(ctx),
680 branch=webutil.nodebranchnodefault(ctx),
681 parent=webutil.parents(ctx),
681 parent=webutil.parents(ctx),
682 child=webutil.children(ctx),
682 child=webutil.children(ctx),
683 diff=diffs)
683 diff=diffs)
684
684
685 diff = filediff
685 diff = filediff
686
686
687 def comparison(web, req, tmpl):
687 def comparison(web, req, tmpl):
688 ctx = webutil.changectx(web.repo, req)
688 ctx = webutil.changectx(web.repo, req)
689 if 'file' not in req.form:
689 if 'file' not in req.form:
690 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
690 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
691 path = webutil.cleanpath(web.repo, req.form['file'][0])
691 path = webutil.cleanpath(web.repo, req.form['file'][0])
692 rename = path in ctx and webutil.renamelink(ctx[path]) or []
692 rename = path in ctx and webutil.renamelink(ctx[path]) or []
693
693
694 parsecontext = lambda v: v == 'full' and -1 or int(v)
694 parsecontext = lambda v: v == 'full' and -1 or int(v)
695 if 'context' in req.form:
695 if 'context' in req.form:
696 context = parsecontext(req.form['context'][0])
696 context = parsecontext(req.form['context'][0])
697 else:
697 else:
698 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
698 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
699
699
700 def filelines(f):
700 def filelines(f):
701 if util.binary(f.data()):
701 if util.binary(f.data()):
702 mt = mimetypes.guess_type(f.path())[0]
702 mt = mimetypes.guess_type(f.path())[0]
703 if not mt:
703 if not mt:
704 mt = 'application/octet-stream'
704 mt = 'application/octet-stream'
705 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
705 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
706 return f.data().splitlines()
706 return f.data().splitlines()
707
707
708 if path in ctx:
708 if path in ctx:
709 fctx = ctx[path]
709 fctx = ctx[path]
710 rightrev = fctx.filerev()
710 rightrev = fctx.filerev()
711 rightnode = fctx.filenode()
711 rightnode = fctx.filenode()
712 rightlines = filelines(fctx)
712 rightlines = filelines(fctx)
713 parents = fctx.parents()
713 parents = fctx.parents()
714 if not parents:
714 if not parents:
715 leftrev = -1
715 leftrev = -1
716 leftnode = nullid
716 leftnode = nullid
717 leftlines = ()
717 leftlines = ()
718 else:
718 else:
719 pfctx = parents[0]
719 pfctx = parents[0]
720 leftrev = pfctx.filerev()
720 leftrev = pfctx.filerev()
721 leftnode = pfctx.filenode()
721 leftnode = pfctx.filenode()
722 leftlines = filelines(pfctx)
722 leftlines = filelines(pfctx)
723 else:
723 else:
724 rightrev = -1
724 rightrev = -1
725 rightnode = nullid
725 rightnode = nullid
726 rightlines = ()
726 rightlines = ()
727 fctx = ctx.parents()[0][path]
727 fctx = ctx.parents()[0][path]
728 leftrev = fctx.filerev()
728 leftrev = fctx.filerev()
729 leftnode = fctx.filenode()
729 leftnode = fctx.filenode()
730 leftlines = filelines(fctx)
730 leftlines = filelines(fctx)
731
731
732 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
732 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
733 return tmpl('filecomparison',
733 return tmpl('filecomparison',
734 file=path,
734 file=path,
735 node=hex(ctx.node()),
735 node=hex(ctx.node()),
736 rev=ctx.rev(),
736 rev=ctx.rev(),
737 date=ctx.date(),
737 date=ctx.date(),
738 desc=ctx.description(),
738 desc=ctx.description(),
739 extra=ctx.extra(),
739 extra=ctx.extra(),
740 author=ctx.user(),
740 author=ctx.user(),
741 rename=rename,
741 rename=rename,
742 branch=webutil.nodebranchnodefault(ctx),
742 branch=webutil.nodebranchnodefault(ctx),
743 parent=webutil.parents(fctx),
743 parent=webutil.parents(fctx),
744 child=webutil.children(fctx),
744 child=webutil.children(fctx),
745 leftrev=leftrev,
745 leftrev=leftrev,
746 leftnode=hex(leftnode),
746 leftnode=hex(leftnode),
747 rightrev=rightrev,
747 rightrev=rightrev,
748 rightnode=hex(rightnode),
748 rightnode=hex(rightnode),
749 comparison=comparison)
749 comparison=comparison)
750
750
751 def annotate(web, req, tmpl):
751 def annotate(web, req, tmpl):
752 fctx = webutil.filectx(web.repo, req)
752 fctx = webutil.filectx(web.repo, req)
753 f = fctx.path()
753 f = fctx.path()
754 parity = paritygen(web.stripecount)
754 parity = paritygen(web.stripecount)
755 diffopts = patch.diffopts(web.repo.ui, untrusted=True, section='annotate')
755 diffopts = patch.diffopts(web.repo.ui, untrusted=True, section='annotate')
756
756
757 def annotate(**map):
757 def annotate(**map):
758 last = None
758 last = None
759 if util.binary(fctx.data()):
759 if util.binary(fctx.data()):
760 mt = (mimetypes.guess_type(fctx.path())[0]
760 mt = (mimetypes.guess_type(fctx.path())[0]
761 or 'application/octet-stream')
761 or 'application/octet-stream')
762 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
762 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
763 '(binary:%s)' % mt)])
763 '(binary:%s)' % mt)])
764 else:
764 else:
765 lines = enumerate(fctx.annotate(follow=True, linenumber=True,
765 lines = enumerate(fctx.annotate(follow=True, linenumber=True,
766 diffopts=diffopts))
766 diffopts=diffopts))
767 for lineno, ((f, targetline), l) in lines:
767 for lineno, ((f, targetline), l) in lines:
768 fnode = f.filenode()
768 fnode = f.filenode()
769
769
770 if last != fnode:
770 if last != fnode:
771 last = fnode
771 last = fnode
772
772
773 yield {"parity": parity.next(),
773 yield {"parity": parity.next(),
774 "node": f.hex(),
774 "node": f.hex(),
775 "rev": f.rev(),
775 "rev": f.rev(),
776 "author": f.user(),
776 "author": f.user(),
777 "desc": f.description(),
777 "desc": f.description(),
778 "extra": f.extra(),
778 "extra": f.extra(),
779 "file": f.path(),
779 "file": f.path(),
780 "targetline": targetline,
780 "targetline": targetline,
781 "line": l,
781 "line": l,
782 "lineid": "l%d" % (lineno + 1),
782 "lineid": "l%d" % (lineno + 1),
783 "linenumber": "% 6d" % (lineno + 1),
783 "linenumber": "% 6d" % (lineno + 1),
784 "revdate": f.date()}
784 "revdate": f.date()}
785
785
786 return tmpl("fileannotate",
786 return tmpl("fileannotate",
787 file=f,
787 file=f,
788 annotate=annotate,
788 annotate=annotate,
789 path=webutil.up(f),
789 path=webutil.up(f),
790 rev=fctx.rev(),
790 rev=fctx.rev(),
791 node=fctx.hex(),
791 node=fctx.hex(),
792 author=fctx.user(),
792 author=fctx.user(),
793 date=fctx.date(),
793 date=fctx.date(),
794 desc=fctx.description(),
794 desc=fctx.description(),
795 extra=fctx.extra(),
795 extra=fctx.extra(),
796 rename=webutil.renamelink(fctx),
796 rename=webutil.renamelink(fctx),
797 branch=webutil.nodebranchnodefault(fctx),
797 branch=webutil.nodebranchnodefault(fctx),
798 parent=webutil.parents(fctx),
798 parent=webutil.parents(fctx),
799 child=webutil.children(fctx),
799 child=webutil.children(fctx),
800 permissions=fctx.manifest().flags(f))
800 permissions=fctx.manifest().flags(f))
801
801
802 def filelog(web, req, tmpl):
802 def filelog(web, req, tmpl):
803
803
804 try:
804 try:
805 fctx = webutil.filectx(web.repo, req)
805 fctx = webutil.filectx(web.repo, req)
806 f = fctx.path()
806 f = fctx.path()
807 fl = fctx.filelog()
807 fl = fctx.filelog()
808 except error.LookupError:
808 except error.LookupError:
809 f = webutil.cleanpath(web.repo, req.form['file'][0])
809 f = webutil.cleanpath(web.repo, req.form['file'][0])
810 fl = web.repo.file(f)
810 fl = web.repo.file(f)
811 numrevs = len(fl)
811 numrevs = len(fl)
812 if not numrevs: # file doesn't exist at all
812 if not numrevs: # file doesn't exist at all
813 raise
813 raise
814 rev = webutil.changectx(web.repo, req).rev()
814 rev = webutil.changectx(web.repo, req).rev()
815 first = fl.linkrev(0)
815 first = fl.linkrev(0)
816 if rev < first: # current rev is from before file existed
816 if rev < first: # current rev is from before file existed
817 raise
817 raise
818 frev = numrevs - 1
818 frev = numrevs - 1
819 while fl.linkrev(frev) > rev:
819 while fl.linkrev(frev) > rev:
820 frev -= 1
820 frev -= 1
821 fctx = web.repo.filectx(f, fl.linkrev(frev))
821 fctx = web.repo.filectx(f, fl.linkrev(frev))
822
822
823 revcount = web.maxshortchanges
823 revcount = web.maxshortchanges
824 if 'revcount' in req.form:
824 if 'revcount' in req.form:
825 revcount = int(req.form.get('revcount', [revcount])[0])
825 revcount = int(req.form.get('revcount', [revcount])[0])
826 revcount = max(revcount, 1)
826 revcount = max(revcount, 1)
827 tmpl.defaults['sessionvars']['revcount'] = revcount
827 tmpl.defaults['sessionvars']['revcount'] = revcount
828
828
829 lessvars = copy.copy(tmpl.defaults['sessionvars'])
829 lessvars = copy.copy(tmpl.defaults['sessionvars'])
830 lessvars['revcount'] = max(revcount / 2, 1)
830 lessvars['revcount'] = max(revcount / 2, 1)
831 morevars = copy.copy(tmpl.defaults['sessionvars'])
831 morevars = copy.copy(tmpl.defaults['sessionvars'])
832 morevars['revcount'] = revcount * 2
832 morevars['revcount'] = revcount * 2
833
833
834 count = fctx.filerev() + 1
834 count = fctx.filerev() + 1
835 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
835 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
836 end = min(count, start + revcount) # last rev on this page
836 end = min(count, start + revcount) # last rev on this page
837 parity = paritygen(web.stripecount, offset=start - end)
837 parity = paritygen(web.stripecount, offset=start - end)
838
838
839 def entries(latestonly, **map):
839 def entries(latestonly):
840 l = []
840 l = []
841
841
842 repo = web.repo
842 repo = web.repo
843 revs = repo.changelog.revs(start, end - 1)
843 revs = repo.changelog.revs(start, end - 1)
844 if latestonly:
844 if latestonly:
845 for r in revs:
845 for r in revs:
846 pass
846 pass
847 revs = (r,)
847 revs = (r,)
848 for i in revs:
848 for i in revs:
849 iterfctx = fctx.filectx(i)
849 iterfctx = fctx.filectx(i)
850
850
851 l.append({"parity": parity.next(),
851 l.append({"parity": parity.next(),
852 "filerev": i,
852 "filerev": i,
853 "file": f,
853 "file": f,
854 "node": iterfctx.hex(),
854 "node": iterfctx.hex(),
855 "author": iterfctx.user(),
855 "author": iterfctx.user(),
856 "date": iterfctx.date(),
856 "date": iterfctx.date(),
857 "rename": webutil.renamelink(iterfctx),
857 "rename": webutil.renamelink(iterfctx),
858 "parent": webutil.parents(iterfctx),
858 "parent": webutil.parents(iterfctx),
859 "child": webutil.children(iterfctx),
859 "child": webutil.children(iterfctx),
860 "desc": iterfctx.description(),
860 "desc": iterfctx.description(),
861 "extra": iterfctx.extra(),
861 "extra": iterfctx.extra(),
862 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
862 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
863 "bookmarks": webutil.nodebookmarksdict(
863 "bookmarks": webutil.nodebookmarksdict(
864 repo, iterfctx.node()),
864 repo, iterfctx.node()),
865 "branch": webutil.nodebranchnodefault(iterfctx),
865 "branch": webutil.nodebranchnodefault(iterfctx),
866 "inbranch": webutil.nodeinbranch(repo, iterfctx),
866 "inbranch": webutil.nodeinbranch(repo, iterfctx),
867 "branches": webutil.nodebranchdict(repo, iterfctx)})
867 "branches": webutil.nodebranchdict(repo, iterfctx)})
868 for e in reversed(l):
868 for e in reversed(l):
869 yield e
869 yield e
870
870
871 revnav = webutil.filerevnav(web.repo, fctx.path())
871 revnav = webutil.filerevnav(web.repo, fctx.path())
872 nav = revnav.gen(end - 1, revcount, count)
872 nav = revnav.gen(end - 1, revcount, count)
873 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
873 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
874 entries=lambda **x: entries(latestonly=False, **x),
874 entries=lambda **x: entries(latestonly=False),
875 latestentry=lambda **x: entries(latestonly=True, **x),
875 latestentry=lambda **x: entries(latestonly=True),
876 revcount=revcount, morevars=morevars, lessvars=lessvars)
876 revcount=revcount, morevars=morevars, lessvars=lessvars)
877
877
878 def archive(web, req, tmpl):
878 def archive(web, req, tmpl):
879 type_ = req.form.get('type', [None])[0]
879 type_ = req.form.get('type', [None])[0]
880 allowed = web.configlist("web", "allow_archive")
880 allowed = web.configlist("web", "allow_archive")
881 key = req.form['node'][0]
881 key = req.form['node'][0]
882
882
883 if type_ not in web.archives:
883 if type_ not in web.archives:
884 msg = 'Unsupported archive type: %s' % type_
884 msg = 'Unsupported archive type: %s' % type_
885 raise ErrorResponse(HTTP_NOT_FOUND, msg)
885 raise ErrorResponse(HTTP_NOT_FOUND, msg)
886
886
887 if not ((type_ in allowed or
887 if not ((type_ in allowed or
888 web.configbool("web", "allow" + type_, False))):
888 web.configbool("web", "allow" + type_, False))):
889 msg = 'Archive type not allowed: %s' % type_
889 msg = 'Archive type not allowed: %s' % type_
890 raise ErrorResponse(HTTP_FORBIDDEN, msg)
890 raise ErrorResponse(HTTP_FORBIDDEN, msg)
891
891
892 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
892 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
893 cnode = web.repo.lookup(key)
893 cnode = web.repo.lookup(key)
894 arch_version = key
894 arch_version = key
895 if cnode == key or key == 'tip':
895 if cnode == key or key == 'tip':
896 arch_version = short(cnode)
896 arch_version = short(cnode)
897 name = "%s-%s" % (reponame, arch_version)
897 name = "%s-%s" % (reponame, arch_version)
898
898
899 ctx = webutil.changectx(web.repo, req)
899 ctx = webutil.changectx(web.repo, req)
900 pats = []
900 pats = []
901 matchfn = None
901 matchfn = None
902 file = req.form.get('file', None)
902 file = req.form.get('file', None)
903 if file:
903 if file:
904 pats = ['path:' + file[0]]
904 pats = ['path:' + file[0]]
905 matchfn = scmutil.match(ctx, pats, default='path')
905 matchfn = scmutil.match(ctx, pats, default='path')
906 if pats:
906 if pats:
907 files = [f for f in ctx.manifest().keys() if matchfn(f)]
907 files = [f for f in ctx.manifest().keys() if matchfn(f)]
908 if not files:
908 if not files:
909 raise ErrorResponse(HTTP_NOT_FOUND,
909 raise ErrorResponse(HTTP_NOT_FOUND,
910 'file(s) not found: %s' % file[0])
910 'file(s) not found: %s' % file[0])
911
911
912 mimetype, artype, extension, encoding = web.archive_specs[type_]
912 mimetype, artype, extension, encoding = web.archive_specs[type_]
913 headers = [
913 headers = [
914 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
914 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
915 ]
915 ]
916 if encoding:
916 if encoding:
917 headers.append(('Content-Encoding', encoding))
917 headers.append(('Content-Encoding', encoding))
918 req.headers.extend(headers)
918 req.headers.extend(headers)
919 req.respond(HTTP_OK, mimetype)
919 req.respond(HTTP_OK, mimetype)
920
920
921 archival.archive(web.repo, req, cnode, artype, prefix=name,
921 archival.archive(web.repo, req, cnode, artype, prefix=name,
922 matchfn=matchfn,
922 matchfn=matchfn,
923 subrepos=web.configbool("web", "archivesubrepos"))
923 subrepos=web.configbool("web", "archivesubrepos"))
924 return []
924 return []
925
925
926
926
927 def static(web, req, tmpl):
927 def static(web, req, tmpl):
928 fname = req.form['file'][0]
928 fname = req.form['file'][0]
929 # a repo owner may set web.static in .hg/hgrc to get any file
929 # a repo owner may set web.static in .hg/hgrc to get any file
930 # readable by the user running the CGI script
930 # readable by the user running the CGI script
931 static = web.config("web", "static", None, untrusted=False)
931 static = web.config("web", "static", None, untrusted=False)
932 if not static:
932 if not static:
933 tp = web.templatepath or templater.templatepath()
933 tp = web.templatepath or templater.templatepath()
934 if isinstance(tp, str):
934 if isinstance(tp, str):
935 tp = [tp]
935 tp = [tp]
936 static = [os.path.join(p, 'static') for p in tp]
936 static = [os.path.join(p, 'static') for p in tp]
937 staticfile(static, fname, req)
937 staticfile(static, fname, req)
938 return []
938 return []
939
939
940 def graph(web, req, tmpl):
940 def graph(web, req, tmpl):
941
941
942 ctx = webutil.changectx(web.repo, req)
942 ctx = webutil.changectx(web.repo, req)
943 rev = ctx.rev()
943 rev = ctx.rev()
944
944
945 bg_height = 39
945 bg_height = 39
946 revcount = web.maxshortchanges
946 revcount = web.maxshortchanges
947 if 'revcount' in req.form:
947 if 'revcount' in req.form:
948 revcount = int(req.form.get('revcount', [revcount])[0])
948 revcount = int(req.form.get('revcount', [revcount])[0])
949 revcount = max(revcount, 1)
949 revcount = max(revcount, 1)
950 tmpl.defaults['sessionvars']['revcount'] = revcount
950 tmpl.defaults['sessionvars']['revcount'] = revcount
951
951
952 lessvars = copy.copy(tmpl.defaults['sessionvars'])
952 lessvars = copy.copy(tmpl.defaults['sessionvars'])
953 lessvars['revcount'] = max(revcount / 2, 1)
953 lessvars['revcount'] = max(revcount / 2, 1)
954 morevars = copy.copy(tmpl.defaults['sessionvars'])
954 morevars = copy.copy(tmpl.defaults['sessionvars'])
955 morevars['revcount'] = revcount * 2
955 morevars['revcount'] = revcount * 2
956
956
957 count = len(web.repo)
957 count = len(web.repo)
958 pos = rev
958 pos = rev
959
959
960 uprev = min(max(0, count - 1), rev + revcount)
960 uprev = min(max(0, count - 1), rev + revcount)
961 downrev = max(0, rev - revcount)
961 downrev = max(0, rev - revcount)
962 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
962 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
963
963
964 tree = []
964 tree = []
965 if pos != -1:
965 if pos != -1:
966 allrevs = web.repo.changelog.revs(pos, 0)
966 allrevs = web.repo.changelog.revs(pos, 0)
967 revs = []
967 revs = []
968 for i in allrevs:
968 for i in allrevs:
969 revs.append(i)
969 revs.append(i)
970 if len(revs) >= revcount:
970 if len(revs) >= revcount:
971 break
971 break
972
972
973 dag = graphmod.dagwalker(web.repo, revs)
973 dag = graphmod.dagwalker(web.repo, revs)
974 tree = list(graphmod.colored(dag, web.repo))
974 tree = list(graphmod.colored(dag, web.repo))
975
975
976 def getcolumns(tree):
976 def getcolumns(tree):
977 cols = 0
977 cols = 0
978 for (id, type, ctx, vtx, edges) in tree:
978 for (id, type, ctx, vtx, edges) in tree:
979 if type != graphmod.CHANGESET:
979 if type != graphmod.CHANGESET:
980 continue
980 continue
981 cols = max(cols, max([edge[0] for edge in edges] or [0]),
981 cols = max(cols, max([edge[0] for edge in edges] or [0]),
982 max([edge[1] for edge in edges] or [0]))
982 max([edge[1] for edge in edges] or [0]))
983 return cols
983 return cols
984
984
985 def graphdata(usetuples, **map):
985 def graphdata(usetuples, **map):
986 data = []
986 data = []
987
987
988 row = 0
988 row = 0
989 for (id, type, ctx, vtx, edges) in tree:
989 for (id, type, ctx, vtx, edges) in tree:
990 if type != graphmod.CHANGESET:
990 if type != graphmod.CHANGESET:
991 continue
991 continue
992 node = str(ctx)
992 node = str(ctx)
993 age = templatefilters.age(ctx.date())
993 age = templatefilters.age(ctx.date())
994 desc = templatefilters.firstline(ctx.description())
994 desc = templatefilters.firstline(ctx.description())
995 desc = cgi.escape(templatefilters.nonempty(desc))
995 desc = cgi.escape(templatefilters.nonempty(desc))
996 user = cgi.escape(templatefilters.person(ctx.user()))
996 user = cgi.escape(templatefilters.person(ctx.user()))
997 branch = cgi.escape(ctx.branch())
997 branch = cgi.escape(ctx.branch())
998 try:
998 try:
999 branchnode = web.repo.branchtip(branch)
999 branchnode = web.repo.branchtip(branch)
1000 except error.RepoLookupError:
1000 except error.RepoLookupError:
1001 branchnode = None
1001 branchnode = None
1002 branch = branch, branchnode == ctx.node()
1002 branch = branch, branchnode == ctx.node()
1003
1003
1004 if usetuples:
1004 if usetuples:
1005 data.append((node, vtx, edges, desc, user, age, branch,
1005 data.append((node, vtx, edges, desc, user, age, branch,
1006 [cgi.escape(x) for x in ctx.tags()],
1006 [cgi.escape(x) for x in ctx.tags()],
1007 [cgi.escape(x) for x in ctx.bookmarks()]))
1007 [cgi.escape(x) for x in ctx.bookmarks()]))
1008 else:
1008 else:
1009 edgedata = [dict(col=edge[0], nextcol=edge[1],
1009 edgedata = [dict(col=edge[0], nextcol=edge[1],
1010 color=(edge[2] - 1) % 6 + 1,
1010 color=(edge[2] - 1) % 6 + 1,
1011 width=edge[3], bcolor=edge[4])
1011 width=edge[3], bcolor=edge[4])
1012 for edge in edges]
1012 for edge in edges]
1013
1013
1014 data.append(
1014 data.append(
1015 dict(node=node,
1015 dict(node=node,
1016 col=vtx[0],
1016 col=vtx[0],
1017 color=(vtx[1] - 1) % 6 + 1,
1017 color=(vtx[1] - 1) % 6 + 1,
1018 edges=edgedata,
1018 edges=edgedata,
1019 row=row,
1019 row=row,
1020 nextrow=row + 1,
1020 nextrow=row + 1,
1021 desc=desc,
1021 desc=desc,
1022 user=user,
1022 user=user,
1023 age=age,
1023 age=age,
1024 bookmarks=webutil.nodebookmarksdict(
1024 bookmarks=webutil.nodebookmarksdict(
1025 web.repo, ctx.node()),
1025 web.repo, ctx.node()),
1026 branches=webutil.nodebranchdict(web.repo, ctx),
1026 branches=webutil.nodebranchdict(web.repo, ctx),
1027 inbranch=webutil.nodeinbranch(web.repo, ctx),
1027 inbranch=webutil.nodeinbranch(web.repo, ctx),
1028 tags=webutil.nodetagsdict(web.repo, ctx.node())))
1028 tags=webutil.nodetagsdict(web.repo, ctx.node())))
1029
1029
1030 row += 1
1030 row += 1
1031
1031
1032 return data
1032 return data
1033
1033
1034 cols = getcolumns(tree)
1034 cols = getcolumns(tree)
1035 rows = len(tree)
1035 rows = len(tree)
1036 canvasheight = (rows + 1) * bg_height - 27
1036 canvasheight = (rows + 1) * bg_height - 27
1037
1037
1038 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
1038 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
1039 lessvars=lessvars, morevars=morevars, downrev=downrev,
1039 lessvars=lessvars, morevars=morevars, downrev=downrev,
1040 cols=cols, rows=rows,
1040 cols=cols, rows=rows,
1041 canvaswidth=(cols + 1) * bg_height,
1041 canvaswidth=(cols + 1) * bg_height,
1042 truecanvasheight=rows * bg_height,
1042 truecanvasheight=rows * bg_height,
1043 canvasheight=canvasheight, bg_height=bg_height,
1043 canvasheight=canvasheight, bg_height=bg_height,
1044 jsdata=lambda **x: graphdata(True, **x),
1044 jsdata=lambda **x: graphdata(True, **x),
1045 nodes=lambda **x: graphdata(False, **x),
1045 nodes=lambda **x: graphdata(False, **x),
1046 node=ctx.hex(), changenav=changenav)
1046 node=ctx.hex(), changenav=changenav)
1047
1047
1048 def _getdoc(e):
1048 def _getdoc(e):
1049 doc = e[0].__doc__
1049 doc = e[0].__doc__
1050 if doc:
1050 if doc:
1051 doc = _(doc).split('\n')[0]
1051 doc = _(doc).split('\n')[0]
1052 else:
1052 else:
1053 doc = _('(no help text available)')
1053 doc = _('(no help text available)')
1054 return doc
1054 return doc
1055
1055
1056 def help(web, req, tmpl):
1056 def help(web, req, tmpl):
1057 from mercurial import commands # avoid cycle
1057 from mercurial import commands # avoid cycle
1058
1058
1059 topicname = req.form.get('node', [None])[0]
1059 topicname = req.form.get('node', [None])[0]
1060 if not topicname:
1060 if not topicname:
1061 def topics(**map):
1061 def topics(**map):
1062 for entries, summary, _ in helpmod.helptable:
1062 for entries, summary, _ in helpmod.helptable:
1063 yield {'topic': entries[0], 'summary': summary}
1063 yield {'topic': entries[0], 'summary': summary}
1064
1064
1065 early, other = [], []
1065 early, other = [], []
1066 primary = lambda s: s.split('|')[0]
1066 primary = lambda s: s.split('|')[0]
1067 for c, e in commands.table.iteritems():
1067 for c, e in commands.table.iteritems():
1068 doc = _getdoc(e)
1068 doc = _getdoc(e)
1069 if 'DEPRECATED' in doc or c.startswith('debug'):
1069 if 'DEPRECATED' in doc or c.startswith('debug'):
1070 continue
1070 continue
1071 cmd = primary(c)
1071 cmd = primary(c)
1072 if cmd.startswith('^'):
1072 if cmd.startswith('^'):
1073 early.append((cmd[1:], doc))
1073 early.append((cmd[1:], doc))
1074 else:
1074 else:
1075 other.append((cmd, doc))
1075 other.append((cmd, doc))
1076
1076
1077 early.sort()
1077 early.sort()
1078 other.sort()
1078 other.sort()
1079
1079
1080 def earlycommands(**map):
1080 def earlycommands(**map):
1081 for c, doc in early:
1081 for c, doc in early:
1082 yield {'topic': c, 'summary': doc}
1082 yield {'topic': c, 'summary': doc}
1083
1083
1084 def othercommands(**map):
1084 def othercommands(**map):
1085 for c, doc in other:
1085 for c, doc in other:
1086 yield {'topic': c, 'summary': doc}
1086 yield {'topic': c, 'summary': doc}
1087
1087
1088 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1088 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1089 othercommands=othercommands, title='Index')
1089 othercommands=othercommands, title='Index')
1090
1090
1091 u = webutil.wsgiui()
1091 u = webutil.wsgiui()
1092 u.verbose = True
1092 u.verbose = True
1093 try:
1093 try:
1094 doc = helpmod.help_(u, topicname)
1094 doc = helpmod.help_(u, topicname)
1095 except error.UnknownCommand:
1095 except error.UnknownCommand:
1096 raise ErrorResponse(HTTP_NOT_FOUND)
1096 raise ErrorResponse(HTTP_NOT_FOUND)
1097 return tmpl('help', topic=topicname, doc=doc)
1097 return tmpl('help', topic=topicname, doc=doc)
General Comments 0
You need to be logged in to leave comments. Login now