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