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