##// END OF EJS Templates
graph: limit the number of displayed changest to a reasonable value
Benoit Allard -
r6704:8251ffb3 default
parent child Browse files
Show More
@@ -1,611 +1,611 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, mimetypes, re, cgi
8 import os, mimetypes, re, cgi
9 import webutil
9 import webutil
10 from mercurial import revlog, archival, templatefilters
10 from mercurial import revlog, archival, templatefilters
11 from mercurial.node import short, hex, nullid
11 from mercurial.node import short, hex, nullid
12 from mercurial.util import binary, datestr
12 from mercurial.util import binary, datestr
13 from mercurial.repo import RepoError
13 from mercurial.repo import RepoError
14 from common import paritygen, staticfile, get_contact, ErrorResponse
14 from common import paritygen, staticfile, get_contact, ErrorResponse
15 from common import HTTP_OK, HTTP_NOT_FOUND
15 from common import HTTP_OK, HTTP_NOT_FOUND
16 from mercurial import graphmod
16 from mercurial import graphmod
17
17
18 # __all__ is populated with the allowed commands. Be sure to add to it if
18 # __all__ is populated with the allowed commands. Be sure to add to it if
19 # you're adding a new command, or the new command won't work.
19 # you're adding a new command, or the new command won't work.
20
20
21 __all__ = [
21 __all__ = [
22 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
22 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
23 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog',
23 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog',
24 'archive', 'static', 'graph',
24 'archive', 'static', 'graph',
25 ]
25 ]
26
26
27 def log(web, req, tmpl):
27 def log(web, req, tmpl):
28 if 'file' in req.form and req.form['file'][0]:
28 if 'file' in req.form and req.form['file'][0]:
29 return filelog(web, req, tmpl)
29 return filelog(web, req, tmpl)
30 else:
30 else:
31 return changelog(web, req, tmpl)
31 return changelog(web, req, tmpl)
32
32
33 def rawfile(web, req, tmpl):
33 def rawfile(web, req, tmpl):
34 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
34 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
35 if not path:
35 if not path:
36 content = manifest(web, req, tmpl)
36 content = manifest(web, req, tmpl)
37 req.respond(HTTP_OK, web.ctype)
37 req.respond(HTTP_OK, web.ctype)
38 return content
38 return content
39
39
40 try:
40 try:
41 fctx = webutil.filectx(web.repo, req)
41 fctx = webutil.filectx(web.repo, req)
42 except revlog.LookupError, inst:
42 except revlog.LookupError, inst:
43 try:
43 try:
44 content = manifest(web, req, tmpl)
44 content = manifest(web, req, tmpl)
45 req.respond(HTTP_OK, web.ctype)
45 req.respond(HTTP_OK, web.ctype)
46 return content
46 return content
47 except ErrorResponse:
47 except ErrorResponse:
48 raise inst
48 raise inst
49
49
50 path = fctx.path()
50 path = fctx.path()
51 text = fctx.data()
51 text = fctx.data()
52 mt = mimetypes.guess_type(path)[0]
52 mt = mimetypes.guess_type(path)[0]
53 if mt is None or binary(text):
53 if mt is None or binary(text):
54 mt = mt or 'application/octet-stream'
54 mt = mt or 'application/octet-stream'
55
55
56 req.respond(HTTP_OK, mt, path, len(text))
56 req.respond(HTTP_OK, mt, path, len(text))
57 return [text]
57 return [text]
58
58
59 def _filerevision(web, tmpl, fctx):
59 def _filerevision(web, tmpl, fctx):
60 f = fctx.path()
60 f = fctx.path()
61 text = fctx.data()
61 text = fctx.data()
62 fl = fctx.filelog()
62 fl = fctx.filelog()
63 n = fctx.filenode()
63 n = fctx.filenode()
64 parity = paritygen(web.stripecount)
64 parity = paritygen(web.stripecount)
65
65
66 if binary(text):
66 if binary(text):
67 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
67 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
68 text = '(binary:%s)' % mt
68 text = '(binary:%s)' % mt
69
69
70 def lines():
70 def lines():
71 for lineno, t in enumerate(text.splitlines(1)):
71 for lineno, t in enumerate(text.splitlines(1)):
72 yield {"line": t,
72 yield {"line": t,
73 "lineid": "l%d" % (lineno + 1),
73 "lineid": "l%d" % (lineno + 1),
74 "linenumber": "% 6d" % (lineno + 1),
74 "linenumber": "% 6d" % (lineno + 1),
75 "parity": parity.next()}
75 "parity": parity.next()}
76
76
77 return tmpl("filerevision",
77 return tmpl("filerevision",
78 file=f,
78 file=f,
79 path=webutil.up(f),
79 path=webutil.up(f),
80 text=lines(),
80 text=lines(),
81 rev=fctx.rev(),
81 rev=fctx.rev(),
82 node=hex(fctx.node()),
82 node=hex(fctx.node()),
83 author=fctx.user(),
83 author=fctx.user(),
84 date=fctx.date(),
84 date=fctx.date(),
85 desc=fctx.description(),
85 desc=fctx.description(),
86 branch=webutil.nodebranchnodefault(fctx),
86 branch=webutil.nodebranchnodefault(fctx),
87 parent=webutil.siblings(fctx.parents()),
87 parent=webutil.siblings(fctx.parents()),
88 child=webutil.siblings(fctx.children()),
88 child=webutil.siblings(fctx.children()),
89 rename=webutil.renamelink(fctx),
89 rename=webutil.renamelink(fctx),
90 permissions=fctx.manifest().flags(f))
90 permissions=fctx.manifest().flags(f))
91
91
92 def file(web, req, tmpl):
92 def file(web, req, tmpl):
93 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
93 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
94 if path:
94 if path:
95 try:
95 try:
96 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
96 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
97 except revlog.LookupError, inst:
97 except revlog.LookupError, inst:
98 pass
98 pass
99
99
100 try:
100 try:
101 return manifest(web, req, tmpl)
101 return manifest(web, req, tmpl)
102 except ErrorResponse:
102 except ErrorResponse:
103 raise inst
103 raise inst
104
104
105 def _search(web, tmpl, query):
105 def _search(web, tmpl, query):
106
106
107 def changelist(**map):
107 def changelist(**map):
108 cl = web.repo.changelog
108 cl = web.repo.changelog
109 count = 0
109 count = 0
110 qw = query.lower().split()
110 qw = query.lower().split()
111
111
112 def revgen():
112 def revgen():
113 for i in xrange(cl.count() - 1, 0, -100):
113 for i in xrange(cl.count() - 1, 0, -100):
114 l = []
114 l = []
115 for j in xrange(max(0, i - 100), i + 1):
115 for j in xrange(max(0, i - 100), i + 1):
116 ctx = web.repo.changectx(j)
116 ctx = web.repo.changectx(j)
117 l.append(ctx)
117 l.append(ctx)
118 l.reverse()
118 l.reverse()
119 for e in l:
119 for e in l:
120 yield e
120 yield e
121
121
122 for ctx in revgen():
122 for ctx in revgen():
123 miss = 0
123 miss = 0
124 for q in qw:
124 for q in qw:
125 if not (q in ctx.user().lower() or
125 if not (q in ctx.user().lower() or
126 q in ctx.description().lower() or
126 q in ctx.description().lower() or
127 q in " ".join(ctx.files()).lower()):
127 q in " ".join(ctx.files()).lower()):
128 miss = 1
128 miss = 1
129 break
129 break
130 if miss:
130 if miss:
131 continue
131 continue
132
132
133 count += 1
133 count += 1
134 n = ctx.node()
134 n = ctx.node()
135 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
135 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
136
136
137 yield tmpl('searchentry',
137 yield tmpl('searchentry',
138 parity=parity.next(),
138 parity=parity.next(),
139 author=ctx.user(),
139 author=ctx.user(),
140 parent=webutil.siblings(ctx.parents()),
140 parent=webutil.siblings(ctx.parents()),
141 child=webutil.siblings(ctx.children()),
141 child=webutil.siblings(ctx.children()),
142 changelogtag=showtags,
142 changelogtag=showtags,
143 desc=ctx.description(),
143 desc=ctx.description(),
144 date=ctx.date(),
144 date=ctx.date(),
145 files=web.listfilediffs(tmpl, ctx.files(), n),
145 files=web.listfilediffs(tmpl, ctx.files(), n),
146 rev=ctx.rev(),
146 rev=ctx.rev(),
147 node=hex(n),
147 node=hex(n),
148 tags=webutil.nodetagsdict(web.repo, n),
148 tags=webutil.nodetagsdict(web.repo, n),
149 inbranch=webutil.nodeinbranch(web.repo, ctx),
149 inbranch=webutil.nodeinbranch(web.repo, ctx),
150 branches=webutil.nodebranchdict(web.repo, ctx))
150 branches=webutil.nodebranchdict(web.repo, ctx))
151
151
152 if count >= web.maxchanges:
152 if count >= web.maxchanges:
153 break
153 break
154
154
155 cl = web.repo.changelog
155 cl = web.repo.changelog
156 parity = paritygen(web.stripecount)
156 parity = paritygen(web.stripecount)
157
157
158 return tmpl('search',
158 return tmpl('search',
159 query=query,
159 query=query,
160 node=hex(cl.tip()),
160 node=hex(cl.tip()),
161 entries=changelist,
161 entries=changelist,
162 archives=web.archivelist("tip"))
162 archives=web.archivelist("tip"))
163
163
164 def changelog(web, req, tmpl, shortlog = False):
164 def changelog(web, req, tmpl, shortlog = False):
165 if 'node' in req.form:
165 if 'node' in req.form:
166 ctx = webutil.changectx(web.repo, req)
166 ctx = webutil.changectx(web.repo, req)
167 else:
167 else:
168 if 'rev' in req.form:
168 if 'rev' in req.form:
169 hi = req.form['rev'][0]
169 hi = req.form['rev'][0]
170 else:
170 else:
171 hi = web.repo.changelog.count() - 1
171 hi = web.repo.changelog.count() - 1
172 try:
172 try:
173 ctx = web.repo.changectx(hi)
173 ctx = web.repo.changectx(hi)
174 except RepoError:
174 except RepoError:
175 return _search(web, tmpl, hi) # XXX redirect to 404 page?
175 return _search(web, tmpl, hi) # XXX redirect to 404 page?
176
176
177 def changelist(limit=0, **map):
177 def changelist(limit=0, **map):
178 cl = web.repo.changelog
178 cl = web.repo.changelog
179 l = [] # build a list in forward order for efficiency
179 l = [] # build a list in forward order for efficiency
180 for i in xrange(start, end):
180 for i in xrange(start, end):
181 ctx = web.repo.changectx(i)
181 ctx = web.repo.changectx(i)
182 n = ctx.node()
182 n = ctx.node()
183 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
183 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
184
184
185 l.insert(0, {"parity": parity.next(),
185 l.insert(0, {"parity": parity.next(),
186 "author": ctx.user(),
186 "author": ctx.user(),
187 "parent": webutil.siblings(ctx.parents(), i - 1),
187 "parent": webutil.siblings(ctx.parents(), i - 1),
188 "child": webutil.siblings(ctx.children(), i + 1),
188 "child": webutil.siblings(ctx.children(), i + 1),
189 "changelogtag": showtags,
189 "changelogtag": showtags,
190 "desc": ctx.description(),
190 "desc": ctx.description(),
191 "date": ctx.date(),
191 "date": ctx.date(),
192 "files": web.listfilediffs(tmpl, ctx.files(), n),
192 "files": web.listfilediffs(tmpl, ctx.files(), n),
193 "rev": i,
193 "rev": i,
194 "node": hex(n),
194 "node": hex(n),
195 "tags": webutil.nodetagsdict(web.repo, n),
195 "tags": webutil.nodetagsdict(web.repo, n),
196 "inbranch": webutil.nodeinbranch(web.repo, ctx),
196 "inbranch": webutil.nodeinbranch(web.repo, ctx),
197 "branches": webutil.nodebranchdict(web.repo, ctx)
197 "branches": webutil.nodebranchdict(web.repo, ctx)
198 })
198 })
199
199
200 if limit > 0:
200 if limit > 0:
201 l = l[:limit]
201 l = l[:limit]
202
202
203 for e in l:
203 for e in l:
204 yield e
204 yield e
205
205
206 maxchanges = shortlog and web.maxshortchanges or web.maxchanges
206 maxchanges = shortlog and web.maxshortchanges or web.maxchanges
207 cl = web.repo.changelog
207 cl = web.repo.changelog
208 count = cl.count()
208 count = cl.count()
209 pos = ctx.rev()
209 pos = ctx.rev()
210 start = max(0, pos - maxchanges + 1)
210 start = max(0, pos - maxchanges + 1)
211 end = min(count, start + maxchanges)
211 end = min(count, start + maxchanges)
212 pos = end - 1
212 pos = end - 1
213 parity = paritygen(web.stripecount, offset=start-end)
213 parity = paritygen(web.stripecount, offset=start-end)
214
214
215 changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
215 changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
216
216
217 return tmpl(shortlog and 'shortlog' or 'changelog',
217 return tmpl(shortlog and 'shortlog' or 'changelog',
218 changenav=changenav,
218 changenav=changenav,
219 node=hex(ctx.node()),
219 node=hex(ctx.node()),
220 rev=pos, changesets=count,
220 rev=pos, changesets=count,
221 entries=lambda **x: changelist(limit=0,**x),
221 entries=lambda **x: changelist(limit=0,**x),
222 latestentry=lambda **x: changelist(limit=1,**x),
222 latestentry=lambda **x: changelist(limit=1,**x),
223 archives=web.archivelist("tip"))
223 archives=web.archivelist("tip"))
224
224
225 def shortlog(web, req, tmpl):
225 def shortlog(web, req, tmpl):
226 return changelog(web, req, tmpl, shortlog = True)
226 return changelog(web, req, tmpl, shortlog = True)
227
227
228 def changeset(web, req, tmpl):
228 def changeset(web, req, tmpl):
229 ctx = webutil.changectx(web.repo, req)
229 ctx = webutil.changectx(web.repo, req)
230 n = ctx.node()
230 n = ctx.node()
231 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n)
231 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n)
232 parents = ctx.parents()
232 parents = ctx.parents()
233 p1 = parents[0].node()
233 p1 = parents[0].node()
234
234
235 files = []
235 files = []
236 parity = paritygen(web.stripecount)
236 parity = paritygen(web.stripecount)
237 for f in ctx.files():
237 for f in ctx.files():
238 files.append(tmpl("filenodelink",
238 files.append(tmpl("filenodelink",
239 node=hex(n), file=f,
239 node=hex(n), file=f,
240 parity=parity.next()))
240 parity=parity.next()))
241
241
242 diffs = web.diff(tmpl, p1, n, None)
242 diffs = web.diff(tmpl, p1, n, None)
243 return tmpl('changeset',
243 return tmpl('changeset',
244 diff=diffs,
244 diff=diffs,
245 rev=ctx.rev(),
245 rev=ctx.rev(),
246 node=hex(n),
246 node=hex(n),
247 parent=webutil.siblings(parents),
247 parent=webutil.siblings(parents),
248 child=webutil.siblings(ctx.children()),
248 child=webutil.siblings(ctx.children()),
249 changesettag=showtags,
249 changesettag=showtags,
250 author=ctx.user(),
250 author=ctx.user(),
251 desc=ctx.description(),
251 desc=ctx.description(),
252 date=ctx.date(),
252 date=ctx.date(),
253 files=files,
253 files=files,
254 archives=web.archivelist(hex(n)),
254 archives=web.archivelist(hex(n)),
255 tags=webutil.nodetagsdict(web.repo, n),
255 tags=webutil.nodetagsdict(web.repo, n),
256 branch=webutil.nodebranchnodefault(ctx),
256 branch=webutil.nodebranchnodefault(ctx),
257 inbranch=webutil.nodeinbranch(web.repo, ctx),
257 inbranch=webutil.nodeinbranch(web.repo, ctx),
258 branches=webutil.nodebranchdict(web.repo, ctx))
258 branches=webutil.nodebranchdict(web.repo, ctx))
259
259
260 rev = changeset
260 rev = changeset
261
261
262 def manifest(web, req, tmpl):
262 def manifest(web, req, tmpl):
263 ctx = webutil.changectx(web.repo, req)
263 ctx = webutil.changectx(web.repo, req)
264 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
264 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
265 mf = ctx.manifest()
265 mf = ctx.manifest()
266 node = ctx.node()
266 node = ctx.node()
267
267
268 files = {}
268 files = {}
269 parity = paritygen(web.stripecount)
269 parity = paritygen(web.stripecount)
270
270
271 if path and path[-1] != "/":
271 if path and path[-1] != "/":
272 path += "/"
272 path += "/"
273 l = len(path)
273 l = len(path)
274 abspath = "/" + path
274 abspath = "/" + path
275
275
276 for f, n in mf.items():
276 for f, n in mf.items():
277 if f[:l] != path:
277 if f[:l] != path:
278 continue
278 continue
279 remain = f[l:]
279 remain = f[l:]
280 if "/" in remain:
280 if "/" in remain:
281 short = remain[:remain.index("/") + 1] # bleah
281 short = remain[:remain.index("/") + 1] # bleah
282 files[short] = (f, None)
282 files[short] = (f, None)
283 else:
283 else:
284 short = os.path.basename(remain)
284 short = os.path.basename(remain)
285 files[short] = (f, n)
285 files[short] = (f, n)
286
286
287 if not files:
287 if not files:
288 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
288 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
289
289
290 def filelist(**map):
290 def filelist(**map):
291 fl = files.keys()
291 fl = files.keys()
292 fl.sort()
292 fl.sort()
293 for f in fl:
293 for f in fl:
294 full, fnode = files[f]
294 full, fnode = files[f]
295 if not fnode:
295 if not fnode:
296 continue
296 continue
297
297
298 fctx = ctx.filectx(full)
298 fctx = ctx.filectx(full)
299 yield {"file": full,
299 yield {"file": full,
300 "parity": parity.next(),
300 "parity": parity.next(),
301 "basename": f,
301 "basename": f,
302 "date": fctx.changectx().date(),
302 "date": fctx.changectx().date(),
303 "size": fctx.size(),
303 "size": fctx.size(),
304 "permissions": mf.flags(full)}
304 "permissions": mf.flags(full)}
305
305
306 def dirlist(**map):
306 def dirlist(**map):
307 fl = files.keys()
307 fl = files.keys()
308 fl.sort()
308 fl.sort()
309 for f in fl:
309 for f in fl:
310 full, fnode = files[f]
310 full, fnode = files[f]
311 if fnode:
311 if fnode:
312 continue
312 continue
313
313
314 yield {"parity": parity.next(),
314 yield {"parity": parity.next(),
315 "path": "%s%s" % (abspath, f),
315 "path": "%s%s" % (abspath, f),
316 "basename": f[:-1]}
316 "basename": f[:-1]}
317
317
318 return tmpl("manifest",
318 return tmpl("manifest",
319 rev=ctx.rev(),
319 rev=ctx.rev(),
320 node=hex(node),
320 node=hex(node),
321 path=abspath,
321 path=abspath,
322 up=webutil.up(abspath),
322 up=webutil.up(abspath),
323 upparity=parity.next(),
323 upparity=parity.next(),
324 fentries=filelist,
324 fentries=filelist,
325 dentries=dirlist,
325 dentries=dirlist,
326 archives=web.archivelist(hex(node)),
326 archives=web.archivelist(hex(node)),
327 tags=webutil.nodetagsdict(web.repo, node),
327 tags=webutil.nodetagsdict(web.repo, node),
328 inbranch=webutil.nodeinbranch(web.repo, ctx),
328 inbranch=webutil.nodeinbranch(web.repo, ctx),
329 branches=webutil.nodebranchdict(web.repo, ctx))
329 branches=webutil.nodebranchdict(web.repo, ctx))
330
330
331 def tags(web, req, tmpl):
331 def tags(web, req, tmpl):
332 i = web.repo.tagslist()
332 i = web.repo.tagslist()
333 i.reverse()
333 i.reverse()
334 parity = paritygen(web.stripecount)
334 parity = paritygen(web.stripecount)
335
335
336 def entries(notip=False,limit=0, **map):
336 def entries(notip=False,limit=0, **map):
337 count = 0
337 count = 0
338 for k, n in i:
338 for k, n in i:
339 if notip and k == "tip":
339 if notip and k == "tip":
340 continue
340 continue
341 if limit > 0 and count >= limit:
341 if limit > 0 and count >= limit:
342 continue
342 continue
343 count = count + 1
343 count = count + 1
344 yield {"parity": parity.next(),
344 yield {"parity": parity.next(),
345 "tag": k,
345 "tag": k,
346 "date": web.repo.changectx(n).date(),
346 "date": web.repo.changectx(n).date(),
347 "node": hex(n)}
347 "node": hex(n)}
348
348
349 return tmpl("tags",
349 return tmpl("tags",
350 node=hex(web.repo.changelog.tip()),
350 node=hex(web.repo.changelog.tip()),
351 entries=lambda **x: entries(False,0, **x),
351 entries=lambda **x: entries(False,0, **x),
352 entriesnotip=lambda **x: entries(True,0, **x),
352 entriesnotip=lambda **x: entries(True,0, **x),
353 latestentry=lambda **x: entries(True,1, **x))
353 latestentry=lambda **x: entries(True,1, **x))
354
354
355 def summary(web, req, tmpl):
355 def summary(web, req, tmpl):
356 i = web.repo.tagslist()
356 i = web.repo.tagslist()
357 i.reverse()
357 i.reverse()
358
358
359 def tagentries(**map):
359 def tagentries(**map):
360 parity = paritygen(web.stripecount)
360 parity = paritygen(web.stripecount)
361 count = 0
361 count = 0
362 for k, n in i:
362 for k, n in i:
363 if k == "tip": # skip tip
363 if k == "tip": # skip tip
364 continue
364 continue
365
365
366 count += 1
366 count += 1
367 if count > 10: # limit to 10 tags
367 if count > 10: # limit to 10 tags
368 break
368 break
369
369
370 yield tmpl("tagentry",
370 yield tmpl("tagentry",
371 parity=parity.next(),
371 parity=parity.next(),
372 tag=k,
372 tag=k,
373 node=hex(n),
373 node=hex(n),
374 date=web.repo.changectx(n).date())
374 date=web.repo.changectx(n).date())
375
375
376 def branches(**map):
376 def branches(**map):
377 parity = paritygen(web.stripecount)
377 parity = paritygen(web.stripecount)
378
378
379 b = web.repo.branchtags()
379 b = web.repo.branchtags()
380 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
380 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
381 l.sort()
381 l.sort()
382
382
383 for r,n,t in l:
383 for r,n,t in l:
384 ctx = web.repo.changectx(n)
384 ctx = web.repo.changectx(n)
385 yield {'parity': parity.next(),
385 yield {'parity': parity.next(),
386 'branch': t,
386 'branch': t,
387 'node': hex(n),
387 'node': hex(n),
388 'date': ctx.date()}
388 'date': ctx.date()}
389
389
390 def changelist(**map):
390 def changelist(**map):
391 parity = paritygen(web.stripecount, offset=start-end)
391 parity = paritygen(web.stripecount, offset=start-end)
392 l = [] # build a list in forward order for efficiency
392 l = [] # build a list in forward order for efficiency
393 for i in xrange(start, end):
393 for i in xrange(start, end):
394 ctx = web.repo.changectx(i)
394 ctx = web.repo.changectx(i)
395 n = ctx.node()
395 n = ctx.node()
396 hn = hex(n)
396 hn = hex(n)
397
397
398 l.insert(0, tmpl(
398 l.insert(0, tmpl(
399 'shortlogentry',
399 'shortlogentry',
400 parity=parity.next(),
400 parity=parity.next(),
401 author=ctx.user(),
401 author=ctx.user(),
402 desc=ctx.description(),
402 desc=ctx.description(),
403 date=ctx.date(),
403 date=ctx.date(),
404 rev=i,
404 rev=i,
405 node=hn,
405 node=hn,
406 tags=webutil.nodetagsdict(web.repo, n),
406 tags=webutil.nodetagsdict(web.repo, n),
407 inbranch=webutil.nodeinbranch(web.repo, ctx),
407 inbranch=webutil.nodeinbranch(web.repo, ctx),
408 branches=webutil.nodebranchdict(web.repo, ctx)))
408 branches=webutil.nodebranchdict(web.repo, ctx)))
409
409
410 yield l
410 yield l
411
411
412 cl = web.repo.changelog
412 cl = web.repo.changelog
413 count = cl.count()
413 count = cl.count()
414 start = max(0, count - web.maxchanges)
414 start = max(0, count - web.maxchanges)
415 end = min(count, start + web.maxchanges)
415 end = min(count, start + web.maxchanges)
416
416
417 return tmpl("summary",
417 return tmpl("summary",
418 desc=web.config("web", "description", "unknown"),
418 desc=web.config("web", "description", "unknown"),
419 owner=get_contact(web.config) or "unknown",
419 owner=get_contact(web.config) or "unknown",
420 lastchange=cl.read(cl.tip())[2],
420 lastchange=cl.read(cl.tip())[2],
421 tags=tagentries,
421 tags=tagentries,
422 branches=branches,
422 branches=branches,
423 shortlog=changelist,
423 shortlog=changelist,
424 node=hex(cl.tip()),
424 node=hex(cl.tip()),
425 archives=web.archivelist("tip"))
425 archives=web.archivelist("tip"))
426
426
427 def filediff(web, req, tmpl):
427 def filediff(web, req, tmpl):
428 fctx = webutil.filectx(web.repo, req)
428 fctx = webutil.filectx(web.repo, req)
429 n = fctx.node()
429 n = fctx.node()
430 path = fctx.path()
430 path = fctx.path()
431 parents = fctx.parents()
431 parents = fctx.parents()
432 p1 = parents and parents[0].node() or nullid
432 p1 = parents and parents[0].node() or nullid
433
433
434 diffs = web.diff(tmpl, p1, n, [path])
434 diffs = web.diff(tmpl, p1, n, [path])
435 return tmpl("filediff",
435 return tmpl("filediff",
436 file=path,
436 file=path,
437 node=hex(n),
437 node=hex(n),
438 rev=fctx.rev(),
438 rev=fctx.rev(),
439 date=fctx.date(),
439 date=fctx.date(),
440 desc=fctx.description(),
440 desc=fctx.description(),
441 author=fctx.user(),
441 author=fctx.user(),
442 rename=webutil.renamelink(fctx),
442 rename=webutil.renamelink(fctx),
443 branch=webutil.nodebranchnodefault(fctx),
443 branch=webutil.nodebranchnodefault(fctx),
444 parent=webutil.siblings(parents),
444 parent=webutil.siblings(parents),
445 child=webutil.siblings(fctx.children()),
445 child=webutil.siblings(fctx.children()),
446 diff=diffs)
446 diff=diffs)
447
447
448 diff = filediff
448 diff = filediff
449
449
450 def annotate(web, req, tmpl):
450 def annotate(web, req, tmpl):
451 fctx = webutil.filectx(web.repo, req)
451 fctx = webutil.filectx(web.repo, req)
452 f = fctx.path()
452 f = fctx.path()
453 n = fctx.filenode()
453 n = fctx.filenode()
454 fl = fctx.filelog()
454 fl = fctx.filelog()
455 parity = paritygen(web.stripecount)
455 parity = paritygen(web.stripecount)
456
456
457 def annotate(**map):
457 def annotate(**map):
458 last = None
458 last = None
459 if binary(fctx.data()):
459 if binary(fctx.data()):
460 mt = (mimetypes.guess_type(fctx.path())[0]
460 mt = (mimetypes.guess_type(fctx.path())[0]
461 or 'application/octet-stream')
461 or 'application/octet-stream')
462 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
462 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
463 '(binary:%s)' % mt)])
463 '(binary:%s)' % mt)])
464 else:
464 else:
465 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
465 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
466 for lineno, ((f, targetline), l) in lines:
466 for lineno, ((f, targetline), l) in lines:
467 fnode = f.filenode()
467 fnode = f.filenode()
468
468
469 if last != fnode:
469 if last != fnode:
470 last = fnode
470 last = fnode
471
471
472 yield {"parity": parity.next(),
472 yield {"parity": parity.next(),
473 "node": hex(f.node()),
473 "node": hex(f.node()),
474 "rev": f.rev(),
474 "rev": f.rev(),
475 "author": f.user(),
475 "author": f.user(),
476 "desc": f.description(),
476 "desc": f.description(),
477 "file": f.path(),
477 "file": f.path(),
478 "targetline": targetline,
478 "targetline": targetline,
479 "line": l,
479 "line": l,
480 "lineid": "l%d" % (lineno + 1),
480 "lineid": "l%d" % (lineno + 1),
481 "linenumber": "% 6d" % (lineno + 1)}
481 "linenumber": "% 6d" % (lineno + 1)}
482
482
483 return tmpl("fileannotate",
483 return tmpl("fileannotate",
484 file=f,
484 file=f,
485 annotate=annotate,
485 annotate=annotate,
486 path=webutil.up(f),
486 path=webutil.up(f),
487 rev=fctx.rev(),
487 rev=fctx.rev(),
488 node=hex(fctx.node()),
488 node=hex(fctx.node()),
489 author=fctx.user(),
489 author=fctx.user(),
490 date=fctx.date(),
490 date=fctx.date(),
491 desc=fctx.description(),
491 desc=fctx.description(),
492 rename=webutil.renamelink(fctx),
492 rename=webutil.renamelink(fctx),
493 branch=webutil.nodebranchnodefault(fctx),
493 branch=webutil.nodebranchnodefault(fctx),
494 parent=webutil.siblings(fctx.parents()),
494 parent=webutil.siblings(fctx.parents()),
495 child=webutil.siblings(fctx.children()),
495 child=webutil.siblings(fctx.children()),
496 permissions=fctx.manifest().flags(f))
496 permissions=fctx.manifest().flags(f))
497
497
498 def filelog(web, req, tmpl):
498 def filelog(web, req, tmpl):
499 fctx = webutil.filectx(web.repo, req)
499 fctx = webutil.filectx(web.repo, req)
500 f = fctx.path()
500 f = fctx.path()
501 fl = fctx.filelog()
501 fl = fctx.filelog()
502 count = fl.count()
502 count = fl.count()
503 pagelen = web.maxshortchanges
503 pagelen = web.maxshortchanges
504 pos = fctx.filerev()
504 pos = fctx.filerev()
505 start = max(0, pos - pagelen + 1)
505 start = max(0, pos - pagelen + 1)
506 end = min(count, start + pagelen)
506 end = min(count, start + pagelen)
507 pos = end - 1
507 pos = end - 1
508 parity = paritygen(web.stripecount, offset=start-end)
508 parity = paritygen(web.stripecount, offset=start-end)
509
509
510 def entries(limit=0, **map):
510 def entries(limit=0, **map):
511 l = []
511 l = []
512
512
513 for i in xrange(start, end):
513 for i in xrange(start, end):
514 ctx = fctx.filectx(i)
514 ctx = fctx.filectx(i)
515 n = fl.node(i)
515 n = fl.node(i)
516
516
517 l.insert(0, {"parity": parity.next(),
517 l.insert(0, {"parity": parity.next(),
518 "filerev": i,
518 "filerev": i,
519 "file": f,
519 "file": f,
520 "node": hex(ctx.node()),
520 "node": hex(ctx.node()),
521 "author": ctx.user(),
521 "author": ctx.user(),
522 "date": ctx.date(),
522 "date": ctx.date(),
523 "rename": webutil.renamelink(fctx),
523 "rename": webutil.renamelink(fctx),
524 "parent": webutil.siblings(fctx.parents()),
524 "parent": webutil.siblings(fctx.parents()),
525 "child": webutil.siblings(fctx.children()),
525 "child": webutil.siblings(fctx.children()),
526 "desc": ctx.description()})
526 "desc": ctx.description()})
527
527
528 if limit > 0:
528 if limit > 0:
529 l = l[:limit]
529 l = l[:limit]
530
530
531 for e in l:
531 for e in l:
532 yield e
532 yield e
533
533
534 nodefunc = lambda x: fctx.filectx(fileid=x)
534 nodefunc = lambda x: fctx.filectx(fileid=x)
535 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
535 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
536 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
536 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
537 entries=lambda **x: entries(limit=0, **x),
537 entries=lambda **x: entries(limit=0, **x),
538 latestentry=lambda **x: entries(limit=1, **x))
538 latestentry=lambda **x: entries(limit=1, **x))
539
539
540
540
541 def archive(web, req, tmpl):
541 def archive(web, req, tmpl):
542 type_ = req.form.get('type', [None])[0]
542 type_ = req.form.get('type', [None])[0]
543 allowed = web.configlist("web", "allow_archive")
543 allowed = web.configlist("web", "allow_archive")
544 key = req.form['node'][0]
544 key = req.form['node'][0]
545
545
546 if not (type_ in web.archives and (type_ in allowed or
546 if not (type_ in web.archives and (type_ in allowed or
547 web.configbool("web", "allow" + type_, False))):
547 web.configbool("web", "allow" + type_, False))):
548 msg = 'Unsupported archive type: %s' % type_
548 msg = 'Unsupported archive type: %s' % type_
549 raise ErrorResponse(HTTP_NOT_FOUND, msg)
549 raise ErrorResponse(HTTP_NOT_FOUND, msg)
550
550
551 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
551 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
552 cnode = web.repo.lookup(key)
552 cnode = web.repo.lookup(key)
553 arch_version = key
553 arch_version = key
554 if cnode == key or key == 'tip':
554 if cnode == key or key == 'tip':
555 arch_version = short(cnode)
555 arch_version = short(cnode)
556 name = "%s-%s" % (reponame, arch_version)
556 name = "%s-%s" % (reponame, arch_version)
557 mimetype, artype, extension, encoding = web.archive_specs[type_]
557 mimetype, artype, extension, encoding = web.archive_specs[type_]
558 headers = [
558 headers = [
559 ('Content-Type', mimetype),
559 ('Content-Type', mimetype),
560 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
560 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
561 ]
561 ]
562 if encoding:
562 if encoding:
563 headers.append(('Content-Encoding', encoding))
563 headers.append(('Content-Encoding', encoding))
564 req.header(headers)
564 req.header(headers)
565 req.respond(HTTP_OK)
565 req.respond(HTTP_OK)
566 archival.archive(web.repo, req, cnode, artype, prefix=name)
566 archival.archive(web.repo, req, cnode, artype, prefix=name)
567 return []
567 return []
568
568
569
569
570 def static(web, req, tmpl):
570 def static(web, req, tmpl):
571 fname = req.form['file'][0]
571 fname = req.form['file'][0]
572 # a repo owner may set web.static in .hg/hgrc to get any file
572 # a repo owner may set web.static in .hg/hgrc to get any file
573 # readable by the user running the CGI script
573 # readable by the user running the CGI script
574 static = web.config("web", "static",
574 static = web.config("web", "static",
575 os.path.join(web.templatepath, "static"),
575 os.path.join(web.templatepath, "static"),
576 untrusted=False)
576 untrusted=False)
577 return [staticfile(static, fname, req)]
577 return [staticfile(static, fname, req)]
578
578
579 def graph(web, req, tmpl):
579 def graph(web, req, tmpl):
580 rev = webutil.changectx(web.repo, req).rev()
580 rev = webutil.changectx(web.repo, req).rev()
581 revcount = int(req.form.get('revcount', [25])[0])
582 bg_height = 39
581 bg_height = 39
583
582
584 max_rev = web.repo.changelog.count() - 1
583 max_rev = web.repo.changelog.count() - 1
584 revcount = min(max_rev, int(req.form.get('revcount', [25])[0]))
585 revnode = web.repo.changelog.node(rev)
585 revnode = web.repo.changelog.node(rev)
586 revnode_hex = hex(revnode)
586 revnode_hex = hex(revnode)
587 uprev = min(max_rev, rev + revcount)
587 uprev = min(max_rev, rev + revcount)
588 downrev = max(0, rev - revcount)
588 downrev = max(0, rev - revcount)
589 lessrev = max(0, rev - revcount / 2)
589 lessrev = max(0, rev - revcount / 2)
590
590
591 maxchanges = web.maxshortchanges or web.maxchanges
591 maxchanges = web.maxshortchanges or web.maxchanges
592 count = web.repo.changelog.count()
592 count = web.repo.changelog.count()
593 changenav = webutil.revnavgen(rev, maxchanges, count, web.repo.changectx)
593 changenav = webutil.revnavgen(rev, maxchanges, count, web.repo.changectx)
594
594
595 tree = list(graphmod.graph(web.repo, rev, rev - revcount))
595 tree = list(graphmod.graph(web.repo, rev, rev - revcount))
596 canvasheight = (len(tree) + 1) * bg_height - 27;
596 canvasheight = (len(tree) + 1) * bg_height - 27;
597
597
598 data = []
598 data = []
599 for i, (ctx, vtx, edges) in enumerate(tree):
599 for i, (ctx, vtx, edges) in enumerate(tree):
600 node = short(ctx.node())
600 node = short(ctx.node())
601 age = templatefilters.age(ctx.date())
601 age = templatefilters.age(ctx.date())
602 desc = templatefilters.firstline(ctx.description())
602 desc = templatefilters.firstline(ctx.description())
603 desc = cgi.escape(desc)
603 desc = cgi.escape(desc)
604 user = cgi.escape(templatefilters.person(ctx.user()))
604 user = cgi.escape(templatefilters.person(ctx.user()))
605 data.append((node, vtx, edges, desc, user, age, ctx.tags()))
605 data.append((node, vtx, edges, desc, user, age, ctx.tags()))
606
606
607 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
607 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
608 lessrev=lessrev, revcountmore=revcount and 2 * revcount or 1,
608 lessrev=lessrev, revcountmore=revcount and 2 * revcount or 1,
609 revcountless=revcount / 2, downrev=downrev,
609 revcountless=revcount / 2, downrev=downrev,
610 canvasheight=canvasheight, bg_height=bg_height,
610 canvasheight=canvasheight, bg_height=bg_height,
611 jsdata=data, node=revnode_hex, changenav=changenav)
611 jsdata=data, node=revnode_hex, changenav=changenav)
General Comments 0
You need to be logged in to leave comments. Login now