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