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