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