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