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