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