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