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