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