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