##// END OF EJS Templates
hgweb: get rid of raw-related code in hgweb.filerevision()
Dirkjan Ochtman -
r5962:0011316f default
parent child Browse files
Show More
@@ -1,908 +1,903 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, mimetypes, re
9 import os, mimetypes, re
10 from mercurial.node import *
10 from mercurial.node import *
11 from mercurial import mdiff, ui, hg, util, archival, patch, hook
11 from mercurial import mdiff, ui, hg, util, archival, patch, hook
12 from mercurial import revlog, templater
12 from mercurial import revlog, templater
13 from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
13 from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
14 from request import wsgirequest
14 from request import wsgirequest
15 import webcommands, protocol
15 import webcommands, protocol
16
16
17 shortcuts = {
17 shortcuts = {
18 'cl': [('cmd', ['changelog']), ('rev', None)],
18 'cl': [('cmd', ['changelog']), ('rev', None)],
19 'sl': [('cmd', ['shortlog']), ('rev', None)],
19 'sl': [('cmd', ['shortlog']), ('rev', None)],
20 'cs': [('cmd', ['changeset']), ('node', None)],
20 'cs': [('cmd', ['changeset']), ('node', None)],
21 'f': [('cmd', ['file']), ('filenode', None)],
21 'f': [('cmd', ['file']), ('filenode', None)],
22 'fl': [('cmd', ['filelog']), ('filenode', None)],
22 'fl': [('cmd', ['filelog']), ('filenode', None)],
23 'fd': [('cmd', ['filediff']), ('node', None)],
23 'fd': [('cmd', ['filediff']), ('node', None)],
24 'fa': [('cmd', ['annotate']), ('filenode', None)],
24 'fa': [('cmd', ['annotate']), ('filenode', None)],
25 'mf': [('cmd', ['manifest']), ('manifest', None)],
25 'mf': [('cmd', ['manifest']), ('manifest', None)],
26 'ca': [('cmd', ['archive']), ('node', None)],
26 'ca': [('cmd', ['archive']), ('node', None)],
27 'tags': [('cmd', ['tags'])],
27 'tags': [('cmd', ['tags'])],
28 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
28 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
29 'static': [('cmd', ['static']), ('file', None)]
29 'static': [('cmd', ['static']), ('file', None)]
30 }
30 }
31
31
32 def _up(p):
32 def _up(p):
33 if p[0] != "/":
33 if p[0] != "/":
34 p = "/" + p
34 p = "/" + p
35 if p[-1] == "/":
35 if p[-1] == "/":
36 p = p[:-1]
36 p = p[:-1]
37 up = os.path.dirname(p)
37 up = os.path.dirname(p)
38 if up == "/":
38 if up == "/":
39 return "/"
39 return "/"
40 return up + "/"
40 return up + "/"
41
41
42 def revnavgen(pos, pagelen, limit, nodefunc):
42 def revnavgen(pos, pagelen, limit, nodefunc):
43 def seq(factor, limit=None):
43 def seq(factor, limit=None):
44 if limit:
44 if limit:
45 yield limit
45 yield limit
46 if limit >= 20 and limit <= 40:
46 if limit >= 20 and limit <= 40:
47 yield 50
47 yield 50
48 else:
48 else:
49 yield 1 * factor
49 yield 1 * factor
50 yield 3 * factor
50 yield 3 * factor
51 for f in seq(factor * 10):
51 for f in seq(factor * 10):
52 yield f
52 yield f
53
53
54 def nav(**map):
54 def nav(**map):
55 l = []
55 l = []
56 last = 0
56 last = 0
57 for f in seq(1, pagelen):
57 for f in seq(1, pagelen):
58 if f < pagelen or f <= last:
58 if f < pagelen or f <= last:
59 continue
59 continue
60 if f > limit:
60 if f > limit:
61 break
61 break
62 last = f
62 last = f
63 if pos + f < limit:
63 if pos + f < limit:
64 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
64 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
65 if pos - f >= 0:
65 if pos - f >= 0:
66 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
66 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
67
67
68 try:
68 try:
69 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
69 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
70
70
71 for label, node in l:
71 for label, node in l:
72 yield {"label": label, "node": node}
72 yield {"label": label, "node": node}
73
73
74 yield {"label": "tip", "node": "tip"}
74 yield {"label": "tip", "node": "tip"}
75 except hg.RepoError:
75 except hg.RepoError:
76 pass
76 pass
77
77
78 return nav
78 return nav
79
79
80 class hgweb(object):
80 class hgweb(object):
81 def __init__(self, repo, name=None):
81 def __init__(self, repo, name=None):
82 if isinstance(repo, str):
82 if isinstance(repo, str):
83 parentui = ui.ui(report_untrusted=False, interactive=False)
83 parentui = ui.ui(report_untrusted=False, interactive=False)
84 self.repo = hg.repository(parentui, repo)
84 self.repo = hg.repository(parentui, repo)
85 else:
85 else:
86 self.repo = repo
86 self.repo = repo
87
87
88 hook.redirect(True)
88 hook.redirect(True)
89 self.mtime = -1
89 self.mtime = -1
90 self.reponame = name
90 self.reponame = name
91 self.archives = 'zip', 'gz', 'bz2'
91 self.archives = 'zip', 'gz', 'bz2'
92 self.stripecount = 1
92 self.stripecount = 1
93 # a repo owner may set web.templates in .hg/hgrc to get any file
93 # a repo owner may set web.templates in .hg/hgrc to get any file
94 # readable by the user running the CGI script
94 # readable by the user running the CGI script
95 self.templatepath = self.config("web", "templates",
95 self.templatepath = self.config("web", "templates",
96 templater.templatepath(),
96 templater.templatepath(),
97 untrusted=False)
97 untrusted=False)
98
98
99 # The CGI scripts are often run by a user different from the repo owner.
99 # The CGI scripts are often run by a user different from the repo owner.
100 # Trust the settings from the .hg/hgrc files by default.
100 # Trust the settings from the .hg/hgrc files by default.
101 def config(self, section, name, default=None, untrusted=True):
101 def config(self, section, name, default=None, untrusted=True):
102 return self.repo.ui.config(section, name, default,
102 return self.repo.ui.config(section, name, default,
103 untrusted=untrusted)
103 untrusted=untrusted)
104
104
105 def configbool(self, section, name, default=False, untrusted=True):
105 def configbool(self, section, name, default=False, untrusted=True):
106 return self.repo.ui.configbool(section, name, default,
106 return self.repo.ui.configbool(section, name, default,
107 untrusted=untrusted)
107 untrusted=untrusted)
108
108
109 def configlist(self, section, name, default=None, untrusted=True):
109 def configlist(self, section, name, default=None, untrusted=True):
110 return self.repo.ui.configlist(section, name, default,
110 return self.repo.ui.configlist(section, name, default,
111 untrusted=untrusted)
111 untrusted=untrusted)
112
112
113 def refresh(self):
113 def refresh(self):
114 mtime = get_mtime(self.repo.root)
114 mtime = get_mtime(self.repo.root)
115 if mtime != self.mtime:
115 if mtime != self.mtime:
116 self.mtime = mtime
116 self.mtime = mtime
117 self.repo = hg.repository(self.repo.ui, self.repo.root)
117 self.repo = hg.repository(self.repo.ui, self.repo.root)
118 self.maxchanges = int(self.config("web", "maxchanges", 10))
118 self.maxchanges = int(self.config("web", "maxchanges", 10))
119 self.stripecount = int(self.config("web", "stripes", 1))
119 self.stripecount = int(self.config("web", "stripes", 1))
120 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
120 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
121 self.maxfiles = int(self.config("web", "maxfiles", 10))
121 self.maxfiles = int(self.config("web", "maxfiles", 10))
122 self.allowpull = self.configbool("web", "allowpull", True)
122 self.allowpull = self.configbool("web", "allowpull", True)
123 self.encoding = self.config("web", "encoding", util._encoding)
123 self.encoding = self.config("web", "encoding", util._encoding)
124
124
125 def run(self):
125 def run(self):
126 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
126 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
127 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
127 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
128 import mercurial.hgweb.wsgicgi as wsgicgi
128 import mercurial.hgweb.wsgicgi as wsgicgi
129 wsgicgi.launch(self)
129 wsgicgi.launch(self)
130
130
131 def __call__(self, env, respond):
131 def __call__(self, env, respond):
132 req = wsgirequest(env, respond)
132 req = wsgirequest(env, respond)
133 self.run_wsgi(req)
133 self.run_wsgi(req)
134 return req
134 return req
135
135
136 def run_wsgi(self, req):
136 def run_wsgi(self, req):
137
137
138 self.refresh()
138 self.refresh()
139
139
140 # expand form shortcuts
140 # expand form shortcuts
141
141
142 for k in shortcuts.iterkeys():
142 for k in shortcuts.iterkeys():
143 if k in req.form:
143 if k in req.form:
144 for name, value in shortcuts[k]:
144 for name, value in shortcuts[k]:
145 if value is None:
145 if value is None:
146 value = req.form[k]
146 value = req.form[k]
147 req.form[name] = value
147 req.form[name] = value
148 del req.form[k]
148 del req.form[k]
149
149
150 # work with CGI variables to create coherent structure
150 # work with CGI variables to create coherent structure
151 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
151 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
152
152
153 req.url = req.env['SCRIPT_NAME']
153 req.url = req.env['SCRIPT_NAME']
154 if not req.url.endswith('/'):
154 if not req.url.endswith('/'):
155 req.url += '/'
155 req.url += '/'
156 if 'REPO_NAME' in req.env:
156 if 'REPO_NAME' in req.env:
157 req.url += req.env['REPO_NAME'] + '/'
157 req.url += req.env['REPO_NAME'] + '/'
158
158
159 if req.env.get('PATH_INFO'):
159 if req.env.get('PATH_INFO'):
160 parts = req.env.get('PATH_INFO').strip('/').split('/')
160 parts = req.env.get('PATH_INFO').strip('/').split('/')
161 repo_parts = req.env.get('REPO_NAME', '').split('/')
161 repo_parts = req.env.get('REPO_NAME', '').split('/')
162 if parts[:len(repo_parts)] == repo_parts:
162 if parts[:len(repo_parts)] == repo_parts:
163 parts = parts[len(repo_parts):]
163 parts = parts[len(repo_parts):]
164 query = '/'.join(parts)
164 query = '/'.join(parts)
165 else:
165 else:
166 query = req.env['QUERY_STRING'].split('&', 1)[0]
166 query = req.env['QUERY_STRING'].split('&', 1)[0]
167 query = query.split(';', 1)[0]
167 query = query.split(';', 1)[0]
168
168
169 # translate user-visible url structure to internal structure
169 # translate user-visible url structure to internal structure
170
170
171 args = query.split('/', 2)
171 args = query.split('/', 2)
172 if 'cmd' not in req.form and args and args[0]:
172 if 'cmd' not in req.form and args and args[0]:
173
173
174 cmd = args.pop(0)
174 cmd = args.pop(0)
175 style = cmd.rfind('-')
175 style = cmd.rfind('-')
176 if style != -1:
176 if style != -1:
177 req.form['style'] = [cmd[:style]]
177 req.form['style'] = [cmd[:style]]
178 cmd = cmd[style+1:]
178 cmd = cmd[style+1:]
179
179
180 # avoid accepting e.g. style parameter as command
180 # avoid accepting e.g. style parameter as command
181 if hasattr(webcommands, cmd) or hasattr(protocol, cmd):
181 if hasattr(webcommands, cmd) or hasattr(protocol, cmd):
182 req.form['cmd'] = [cmd]
182 req.form['cmd'] = [cmd]
183
183
184 if args and args[0]:
184 if args and args[0]:
185 node = args.pop(0)
185 node = args.pop(0)
186 req.form['node'] = [node]
186 req.form['node'] = [node]
187 if args:
187 if args:
188 req.form['file'] = args
188 req.form['file'] = args
189
189
190 if cmd == 'static':
190 if cmd == 'static':
191 req.form['file'] = req.form['node']
191 req.form['file'] = req.form['node']
192 elif cmd == 'archive':
192 elif cmd == 'archive':
193 fn = req.form['node'][0]
193 fn = req.form['node'][0]
194 for type_, spec in self.archive_specs.iteritems():
194 for type_, spec in self.archive_specs.iteritems():
195 ext = spec[2]
195 ext = spec[2]
196 if fn.endswith(ext):
196 if fn.endswith(ext):
197 req.form['node'] = [fn[:-len(ext)]]
197 req.form['node'] = [fn[:-len(ext)]]
198 req.form['type'] = [type_]
198 req.form['type'] = [type_]
199
199
200 # actually process the request
200 # actually process the request
201
201
202 try:
202 try:
203
203
204 cmd = req.form.get('cmd', [''])[0]
204 cmd = req.form.get('cmd', [''])[0]
205 if hasattr(protocol, cmd):
205 if hasattr(protocol, cmd):
206 method = getattr(protocol, cmd)
206 method = getattr(protocol, cmd)
207 method(self, req)
207 method(self, req)
208 else:
208 else:
209
209
210 tmpl = self.templater(req)
210 tmpl = self.templater(req)
211 if cmd == '':
211 if cmd == '':
212 req.form['cmd'] = [tmpl.cache['default']]
212 req.form['cmd'] = [tmpl.cache['default']]
213 cmd = req.form['cmd'][0]
213 cmd = req.form['cmd'][0]
214
214
215 if cmd == 'file' and 'raw' in req.form.get('style', []):
215 if cmd == 'file' and 'raw' in req.form.get('style', []):
216 webcommands.rawfile(self, req, tmpl)
216 webcommands.rawfile(self, req, tmpl)
217 else:
217 else:
218 getattr(webcommands, cmd)(self, req, tmpl)
218 getattr(webcommands, cmd)(self, req, tmpl)
219
219
220 del tmpl
220 del tmpl
221
221
222 except revlog.LookupError, err:
222 except revlog.LookupError, err:
223 req.respond(404, tmpl(
223 req.respond(404, tmpl(
224 'error', error='revision not found: %s' % err.name))
224 'error', error='revision not found: %s' % err.name))
225 except (hg.RepoError, revlog.RevlogError), inst:
225 except (hg.RepoError, revlog.RevlogError), inst:
226 req.respond('500 Internal Server Error',
226 req.respond('500 Internal Server Error',
227 tmpl('error', error=str(inst)))
227 tmpl('error', error=str(inst)))
228 except ErrorResponse, inst:
228 except ErrorResponse, inst:
229 req.respond(inst.code, tmpl('error', error=inst.message))
229 req.respond(inst.code, tmpl('error', error=inst.message))
230 except AttributeError:
230 except AttributeError:
231 req.respond(400, tmpl('error', error='No such method: ' + cmd))
231 req.respond(400, tmpl('error', error='No such method: ' + cmd))
232
232
233 def templater(self, req):
233 def templater(self, req):
234
234
235 # determine scheme, port and server name
235 # determine scheme, port and server name
236 # this is needed to create absolute urls
236 # this is needed to create absolute urls
237
237
238 proto = req.env.get('wsgi.url_scheme')
238 proto = req.env.get('wsgi.url_scheme')
239 if proto == 'https':
239 if proto == 'https':
240 proto = 'https'
240 proto = 'https'
241 default_port = "443"
241 default_port = "443"
242 else:
242 else:
243 proto = 'http'
243 proto = 'http'
244 default_port = "80"
244 default_port = "80"
245
245
246 port = req.env["SERVER_PORT"]
246 port = req.env["SERVER_PORT"]
247 port = port != default_port and (":" + port) or ""
247 port = port != default_port and (":" + port) or ""
248 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
248 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
249 staticurl = self.config("web", "staticurl") or req.url + 'static/'
249 staticurl = self.config("web", "staticurl") or req.url + 'static/'
250 if not staticurl.endswith('/'):
250 if not staticurl.endswith('/'):
251 staticurl += '/'
251 staticurl += '/'
252
252
253 # some functions for the templater
253 # some functions for the templater
254
254
255 def header(**map):
255 def header(**map):
256 ctype = tmpl('mimetype', encoding=self.encoding)
256 ctype = tmpl('mimetype', encoding=self.encoding)
257 req.httphdr(templater.stringify(ctype))
257 req.httphdr(templater.stringify(ctype))
258 yield tmpl('header', encoding=self.encoding, **map)
258 yield tmpl('header', encoding=self.encoding, **map)
259
259
260 def footer(**map):
260 def footer(**map):
261 yield tmpl("footer", **map)
261 yield tmpl("footer", **map)
262
262
263 def motd(**map):
263 def motd(**map):
264 yield self.config("web", "motd", "")
264 yield self.config("web", "motd", "")
265
265
266 def sessionvars(**map):
266 def sessionvars(**map):
267 fields = []
267 fields = []
268 if 'style' in req.form:
268 if 'style' in req.form:
269 style = req.form['style'][0]
269 style = req.form['style'][0]
270 if style != self.config('web', 'style', ''):
270 if style != self.config('web', 'style', ''):
271 fields.append(('style', style))
271 fields.append(('style', style))
272
272
273 separator = req.url[-1] == '?' and ';' or '?'
273 separator = req.url[-1] == '?' and ';' or '?'
274 for name, value in fields:
274 for name, value in fields:
275 yield dict(name=name, value=value, separator=separator)
275 yield dict(name=name, value=value, separator=separator)
276 separator = ';'
276 separator = ';'
277
277
278 # figure out which style to use
278 # figure out which style to use
279
279
280 style = self.config("web", "style", "")
280 style = self.config("web", "style", "")
281 if 'style' in req.form:
281 if 'style' in req.form:
282 style = req.form['style'][0]
282 style = req.form['style'][0]
283 mapfile = style_map(self.templatepath, style)
283 mapfile = style_map(self.templatepath, style)
284
284
285 if not self.reponame:
285 if not self.reponame:
286 self.reponame = (self.config("web", "name")
286 self.reponame = (self.config("web", "name")
287 or req.env.get('REPO_NAME')
287 or req.env.get('REPO_NAME')
288 or req.url.strip('/') or self.repo.root)
288 or req.url.strip('/') or self.repo.root)
289
289
290 # create the templater
290 # create the templater
291
291
292 tmpl = templater.templater(mapfile, templater.common_filters,
292 tmpl = templater.templater(mapfile, templater.common_filters,
293 defaults={"url": req.url,
293 defaults={"url": req.url,
294 "staticurl": staticurl,
294 "staticurl": staticurl,
295 "urlbase": urlbase,
295 "urlbase": urlbase,
296 "repo": self.reponame,
296 "repo": self.reponame,
297 "header": header,
297 "header": header,
298 "footer": footer,
298 "footer": footer,
299 "motd": motd,
299 "motd": motd,
300 "sessionvars": sessionvars
300 "sessionvars": sessionvars
301 })
301 })
302 return tmpl
302 return tmpl
303
303
304 def archivelist(self, nodeid):
304 def archivelist(self, nodeid):
305 allowed = self.configlist("web", "allow_archive")
305 allowed = self.configlist("web", "allow_archive")
306 for i, spec in self.archive_specs.iteritems():
306 for i, spec in self.archive_specs.iteritems():
307 if i in allowed or self.configbool("web", "allow" + i):
307 if i in allowed or self.configbool("web", "allow" + i):
308 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
308 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
309
309
310 def listfilediffs(self, tmpl, files, changeset):
310 def listfilediffs(self, tmpl, files, changeset):
311 for f in files[:self.maxfiles]:
311 for f in files[:self.maxfiles]:
312 yield tmpl("filedifflink", node=hex(changeset), file=f)
312 yield tmpl("filedifflink", node=hex(changeset), file=f)
313 if len(files) > self.maxfiles:
313 if len(files) > self.maxfiles:
314 yield tmpl("fileellipses")
314 yield tmpl("fileellipses")
315
315
316 def siblings(self, siblings=[], hiderev=None, **args):
316 def siblings(self, siblings=[], hiderev=None, **args):
317 siblings = [s for s in siblings if s.node() != nullid]
317 siblings = [s for s in siblings if s.node() != nullid]
318 if len(siblings) == 1 and siblings[0].rev() == hiderev:
318 if len(siblings) == 1 and siblings[0].rev() == hiderev:
319 return
319 return
320 for s in siblings:
320 for s in siblings:
321 d = {'node': hex(s.node()), 'rev': s.rev()}
321 d = {'node': hex(s.node()), 'rev': s.rev()}
322 if hasattr(s, 'path'):
322 if hasattr(s, 'path'):
323 d['file'] = s.path()
323 d['file'] = s.path()
324 d.update(args)
324 d.update(args)
325 yield d
325 yield d
326
326
327 def renamelink(self, fl, node):
327 def renamelink(self, fl, node):
328 r = fl.renamed(node)
328 r = fl.renamed(node)
329 if r:
329 if r:
330 return [dict(file=r[0], node=hex(r[1]))]
330 return [dict(file=r[0], node=hex(r[1]))]
331 return []
331 return []
332
332
333 def nodetagsdict(self, node):
333 def nodetagsdict(self, node):
334 return [{"name": i} for i in self.repo.nodetags(node)]
334 return [{"name": i} for i in self.repo.nodetags(node)]
335
335
336 def nodebranchdict(self, ctx):
336 def nodebranchdict(self, ctx):
337 branches = []
337 branches = []
338 branch = ctx.branch()
338 branch = ctx.branch()
339 # If this is an empty repo, ctx.node() == nullid,
339 # If this is an empty repo, ctx.node() == nullid,
340 # ctx.branch() == 'default', but branchtags() is
340 # ctx.branch() == 'default', but branchtags() is
341 # an empty dict. Using dict.get avoids a traceback.
341 # an empty dict. Using dict.get avoids a traceback.
342 if self.repo.branchtags().get(branch) == ctx.node():
342 if self.repo.branchtags().get(branch) == ctx.node():
343 branches.append({"name": branch})
343 branches.append({"name": branch})
344 return branches
344 return branches
345
345
346 def showtag(self, tmpl, t1, node=nullid, **args):
346 def showtag(self, tmpl, t1, node=nullid, **args):
347 for t in self.repo.nodetags(node):
347 for t in self.repo.nodetags(node):
348 yield tmpl(t1, tag=t, **args)
348 yield tmpl(t1, tag=t, **args)
349
349
350 def diff(self, tmpl, node1, node2, files):
350 def diff(self, tmpl, node1, node2, files):
351 def filterfiles(filters, files):
351 def filterfiles(filters, files):
352 l = [x for x in files if x in filters]
352 l = [x for x in files if x in filters]
353
353
354 for t in filters:
354 for t in filters:
355 if t and t[-1] != os.sep:
355 if t and t[-1] != os.sep:
356 t += os.sep
356 t += os.sep
357 l += [x for x in files if x.startswith(t)]
357 l += [x for x in files if x.startswith(t)]
358 return l
358 return l
359
359
360 parity = paritygen(self.stripecount)
360 parity = paritygen(self.stripecount)
361 def diffblock(diff, f, fn):
361 def diffblock(diff, f, fn):
362 yield tmpl("diffblock",
362 yield tmpl("diffblock",
363 lines=prettyprintlines(diff),
363 lines=prettyprintlines(diff),
364 parity=parity.next(),
364 parity=parity.next(),
365 file=f,
365 file=f,
366 filenode=hex(fn or nullid))
366 filenode=hex(fn or nullid))
367
367
368 def prettyprintlines(diff):
368 def prettyprintlines(diff):
369 for l in diff.splitlines(1):
369 for l in diff.splitlines(1):
370 if l.startswith('+'):
370 if l.startswith('+'):
371 yield tmpl("difflineplus", line=l)
371 yield tmpl("difflineplus", line=l)
372 elif l.startswith('-'):
372 elif l.startswith('-'):
373 yield tmpl("difflineminus", line=l)
373 yield tmpl("difflineminus", line=l)
374 elif l.startswith('@'):
374 elif l.startswith('@'):
375 yield tmpl("difflineat", line=l)
375 yield tmpl("difflineat", line=l)
376 else:
376 else:
377 yield tmpl("diffline", line=l)
377 yield tmpl("diffline", line=l)
378
378
379 r = self.repo
379 r = self.repo
380 c1 = r.changectx(node1)
380 c1 = r.changectx(node1)
381 c2 = r.changectx(node2)
381 c2 = r.changectx(node2)
382 date1 = util.datestr(c1.date())
382 date1 = util.datestr(c1.date())
383 date2 = util.datestr(c2.date())
383 date2 = util.datestr(c2.date())
384
384
385 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
385 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
386 if files:
386 if files:
387 modified, added, removed = map(lambda x: filterfiles(files, x),
387 modified, added, removed = map(lambda x: filterfiles(files, x),
388 (modified, added, removed))
388 (modified, added, removed))
389
389
390 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
390 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
391 for f in modified:
391 for f in modified:
392 to = c1.filectx(f).data()
392 to = c1.filectx(f).data()
393 tn = c2.filectx(f).data()
393 tn = c2.filectx(f).data()
394 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
394 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
395 opts=diffopts), f, tn)
395 opts=diffopts), f, tn)
396 for f in added:
396 for f in added:
397 to = None
397 to = None
398 tn = c2.filectx(f).data()
398 tn = c2.filectx(f).data()
399 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
399 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
400 opts=diffopts), f, tn)
400 opts=diffopts), f, tn)
401 for f in removed:
401 for f in removed:
402 to = c1.filectx(f).data()
402 to = c1.filectx(f).data()
403 tn = None
403 tn = None
404 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
404 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
405 opts=diffopts), f, tn)
405 opts=diffopts), f, tn)
406
406
407 def changelog(self, tmpl, ctx, shortlog=False):
407 def changelog(self, tmpl, ctx, shortlog=False):
408 def changelist(limit=0,**map):
408 def changelist(limit=0,**map):
409 cl = self.repo.changelog
409 cl = self.repo.changelog
410 l = [] # build a list in forward order for efficiency
410 l = [] # build a list in forward order for efficiency
411 for i in xrange(start, end):
411 for i in xrange(start, end):
412 ctx = self.repo.changectx(i)
412 ctx = self.repo.changectx(i)
413 n = ctx.node()
413 n = ctx.node()
414
414
415 l.insert(0, {"parity": parity.next(),
415 l.insert(0, {"parity": parity.next(),
416 "author": ctx.user(),
416 "author": ctx.user(),
417 "parent": self.siblings(ctx.parents(), i - 1),
417 "parent": self.siblings(ctx.parents(), i - 1),
418 "child": self.siblings(ctx.children(), i + 1),
418 "child": self.siblings(ctx.children(), i + 1),
419 "changelogtag": self.showtag("changelogtag",n),
419 "changelogtag": self.showtag("changelogtag",n),
420 "desc": ctx.description(),
420 "desc": ctx.description(),
421 "date": ctx.date(),
421 "date": ctx.date(),
422 "files": self.listfilediffs(tmpl, ctx.files(), n),
422 "files": self.listfilediffs(tmpl, ctx.files(), n),
423 "rev": i,
423 "rev": i,
424 "node": hex(n),
424 "node": hex(n),
425 "tags": self.nodetagsdict(n),
425 "tags": self.nodetagsdict(n),
426 "branches": self.nodebranchdict(ctx)})
426 "branches": self.nodebranchdict(ctx)})
427
427
428 if limit > 0:
428 if limit > 0:
429 l = l[:limit]
429 l = l[:limit]
430
430
431 for e in l:
431 for e in l:
432 yield e
432 yield e
433
433
434 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
434 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
435 cl = self.repo.changelog
435 cl = self.repo.changelog
436 count = cl.count()
436 count = cl.count()
437 pos = ctx.rev()
437 pos = ctx.rev()
438 start = max(0, pos - maxchanges + 1)
438 start = max(0, pos - maxchanges + 1)
439 end = min(count, start + maxchanges)
439 end = min(count, start + maxchanges)
440 pos = end - 1
440 pos = end - 1
441 parity = paritygen(self.stripecount, offset=start-end)
441 parity = paritygen(self.stripecount, offset=start-end)
442
442
443 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
443 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
444
444
445 return tmpl(shortlog and 'shortlog' or 'changelog',
445 return tmpl(shortlog and 'shortlog' or 'changelog',
446 changenav=changenav,
446 changenav=changenav,
447 node=hex(cl.tip()),
447 node=hex(cl.tip()),
448 rev=pos, changesets=count,
448 rev=pos, changesets=count,
449 entries=lambda **x: changelist(limit=0,**x),
449 entries=lambda **x: changelist(limit=0,**x),
450 latestentry=lambda **x: changelist(limit=1,**x),
450 latestentry=lambda **x: changelist(limit=1,**x),
451 archives=self.archivelist("tip"))
451 archives=self.archivelist("tip"))
452
452
453 def search(self, tmpl, query):
453 def search(self, tmpl, query):
454
454
455 def changelist(**map):
455 def changelist(**map):
456 cl = self.repo.changelog
456 cl = self.repo.changelog
457 count = 0
457 count = 0
458 qw = query.lower().split()
458 qw = query.lower().split()
459
459
460 def revgen():
460 def revgen():
461 for i in xrange(cl.count() - 1, 0, -100):
461 for i in xrange(cl.count() - 1, 0, -100):
462 l = []
462 l = []
463 for j in xrange(max(0, i - 100), i):
463 for j in xrange(max(0, i - 100), i):
464 ctx = self.repo.changectx(j)
464 ctx = self.repo.changectx(j)
465 l.append(ctx)
465 l.append(ctx)
466 l.reverse()
466 l.reverse()
467 for e in l:
467 for e in l:
468 yield e
468 yield e
469
469
470 for ctx in revgen():
470 for ctx in revgen():
471 miss = 0
471 miss = 0
472 for q in qw:
472 for q in qw:
473 if not (q in ctx.user().lower() or
473 if not (q in ctx.user().lower() or
474 q in ctx.description().lower() or
474 q in ctx.description().lower() or
475 q in " ".join(ctx.files()).lower()):
475 q in " ".join(ctx.files()).lower()):
476 miss = 1
476 miss = 1
477 break
477 break
478 if miss:
478 if miss:
479 continue
479 continue
480
480
481 count += 1
481 count += 1
482 n = ctx.node()
482 n = ctx.node()
483
483
484 yield tmpl('searchentry',
484 yield tmpl('searchentry',
485 parity=parity.next(),
485 parity=parity.next(),
486 author=ctx.user(),
486 author=ctx.user(),
487 parent=self.siblings(ctx.parents()),
487 parent=self.siblings(ctx.parents()),
488 child=self.siblings(ctx.children()),
488 child=self.siblings(ctx.children()),
489 changelogtag=self.showtag("changelogtag",n),
489 changelogtag=self.showtag("changelogtag",n),
490 desc=ctx.description(),
490 desc=ctx.description(),
491 date=ctx.date(),
491 date=ctx.date(),
492 files=self.listfilediffs(tmpl, ctx.files(), n),
492 files=self.listfilediffs(tmpl, ctx.files(), n),
493 rev=ctx.rev(),
493 rev=ctx.rev(),
494 node=hex(n),
494 node=hex(n),
495 tags=self.nodetagsdict(n),
495 tags=self.nodetagsdict(n),
496 branches=self.nodebranchdict(ctx))
496 branches=self.nodebranchdict(ctx))
497
497
498 if count >= self.maxchanges:
498 if count >= self.maxchanges:
499 break
499 break
500
500
501 cl = self.repo.changelog
501 cl = self.repo.changelog
502 parity = paritygen(self.stripecount)
502 parity = paritygen(self.stripecount)
503
503
504 return tmpl('search',
504 return tmpl('search',
505 query=query,
505 query=query,
506 node=hex(cl.tip()),
506 node=hex(cl.tip()),
507 entries=changelist,
507 entries=changelist,
508 archives=self.archivelist("tip"))
508 archives=self.archivelist("tip"))
509
509
510 def changeset(self, tmpl, ctx):
510 def changeset(self, tmpl, ctx):
511 n = ctx.node()
511 n = ctx.node()
512 parents = ctx.parents()
512 parents = ctx.parents()
513 p1 = parents[0].node()
513 p1 = parents[0].node()
514
514
515 files = []
515 files = []
516 parity = paritygen(self.stripecount)
516 parity = paritygen(self.stripecount)
517 for f in ctx.files():
517 for f in ctx.files():
518 files.append(tmpl("filenodelink",
518 files.append(tmpl("filenodelink",
519 node=hex(n), file=f,
519 node=hex(n), file=f,
520 parity=parity.next()))
520 parity=parity.next()))
521
521
522 def diff(**map):
522 def diff(**map):
523 yield self.diff(tmpl, p1, n, None)
523 yield self.diff(tmpl, p1, n, None)
524
524
525 return tmpl('changeset',
525 return tmpl('changeset',
526 diff=diff,
526 diff=diff,
527 rev=ctx.rev(),
527 rev=ctx.rev(),
528 node=hex(n),
528 node=hex(n),
529 parent=self.siblings(parents),
529 parent=self.siblings(parents),
530 child=self.siblings(ctx.children()),
530 child=self.siblings(ctx.children()),
531 changesettag=self.showtag("changesettag",n),
531 changesettag=self.showtag("changesettag",n),
532 author=ctx.user(),
532 author=ctx.user(),
533 desc=ctx.description(),
533 desc=ctx.description(),
534 date=ctx.date(),
534 date=ctx.date(),
535 files=files,
535 files=files,
536 archives=self.archivelist(hex(n)),
536 archives=self.archivelist(hex(n)),
537 tags=self.nodetagsdict(n),
537 tags=self.nodetagsdict(n),
538 branches=self.nodebranchdict(ctx))
538 branches=self.nodebranchdict(ctx))
539
539
540 def filelog(self, tmpl, fctx):
540 def filelog(self, tmpl, fctx):
541 f = fctx.path()
541 f = fctx.path()
542 fl = fctx.filelog()
542 fl = fctx.filelog()
543 count = fl.count()
543 count = fl.count()
544 pagelen = self.maxshortchanges
544 pagelen = self.maxshortchanges
545 pos = fctx.filerev()
545 pos = fctx.filerev()
546 start = max(0, pos - pagelen + 1)
546 start = max(0, pos - pagelen + 1)
547 end = min(count, start + pagelen)
547 end = min(count, start + pagelen)
548 pos = end - 1
548 pos = end - 1
549 parity = paritygen(self.stripecount, offset=start-end)
549 parity = paritygen(self.stripecount, offset=start-end)
550
550
551 def entries(limit=0, **map):
551 def entries(limit=0, **map):
552 l = []
552 l = []
553
553
554 for i in xrange(start, end):
554 for i in xrange(start, end):
555 ctx = fctx.filectx(i)
555 ctx = fctx.filectx(i)
556 n = fl.node(i)
556 n = fl.node(i)
557
557
558 l.insert(0, {"parity": parity.next(),
558 l.insert(0, {"parity": parity.next(),
559 "filerev": i,
559 "filerev": i,
560 "file": f,
560 "file": f,
561 "node": hex(ctx.node()),
561 "node": hex(ctx.node()),
562 "author": ctx.user(),
562 "author": ctx.user(),
563 "date": ctx.date(),
563 "date": ctx.date(),
564 "rename": self.renamelink(fl, n),
564 "rename": self.renamelink(fl, n),
565 "parent": self.siblings(fctx.parents()),
565 "parent": self.siblings(fctx.parents()),
566 "child": self.siblings(fctx.children()),
566 "child": self.siblings(fctx.children()),
567 "desc": ctx.description()})
567 "desc": ctx.description()})
568
568
569 if limit > 0:
569 if limit > 0:
570 l = l[:limit]
570 l = l[:limit]
571
571
572 for e in l:
572 for e in l:
573 yield e
573 yield e
574
574
575 nodefunc = lambda x: fctx.filectx(fileid=x)
575 nodefunc = lambda x: fctx.filectx(fileid=x)
576 nav = revnavgen(pos, pagelen, count, nodefunc)
576 nav = revnavgen(pos, pagelen, count, nodefunc)
577 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
577 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
578 entries=lambda **x: entries(limit=0, **x),
578 entries=lambda **x: entries(limit=0, **x),
579 latestentry=lambda **x: entries(limit=1, **x))
579 latestentry=lambda **x: entries(limit=1, **x))
580
580
581 def filerevision(self, tmpl, fctx):
581 def filerevision(self, tmpl, fctx):
582 f = fctx.path()
582 f = fctx.path()
583 text = fctx.data()
583 text = fctx.data()
584 fl = fctx.filelog()
584 fl = fctx.filelog()
585 n = fctx.filenode()
585 n = fctx.filenode()
586 parity = paritygen(self.stripecount)
586 parity = paritygen(self.stripecount)
587
587
588 mt = mimetypes.guess_type(f)[0]
589 rawtext = text
590 if util.binary(text):
588 if util.binary(text):
591 mt = mt or 'application/octet-stream'
589 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
592 text = "(binary:%s)" % mt
590 text = '(binary:%s)' % mt
593 mt = mt or 'text/plain'
594
591
595 def lines():
592 def lines():
596 for l, t in enumerate(text.splitlines(1)):
593 for l, t in enumerate(text.splitlines(1)):
597 yield {"line": t,
594 yield {"line": t,
598 "linenumber": "% 6d" % (l + 1),
595 "linenumber": "% 6d" % (l + 1),
599 "parity": parity.next()}
596 "parity": parity.next()}
600
597
601 return tmpl("filerevision",
598 return tmpl("filerevision",
602 file=f,
599 file=f,
603 path=_up(f),
600 path=_up(f),
604 text=lines(),
601 text=lines(),
605 raw=rawtext,
606 mimetype=mt,
607 rev=fctx.rev(),
602 rev=fctx.rev(),
608 node=hex(fctx.node()),
603 node=hex(fctx.node()),
609 author=fctx.user(),
604 author=fctx.user(),
610 date=fctx.date(),
605 date=fctx.date(),
611 desc=fctx.description(),
606 desc=fctx.description(),
612 parent=self.siblings(fctx.parents()),
607 parent=self.siblings(fctx.parents()),
613 child=self.siblings(fctx.children()),
608 child=self.siblings(fctx.children()),
614 rename=self.renamelink(fl, n),
609 rename=self.renamelink(fl, n),
615 permissions=fctx.manifest().flags(f))
610 permissions=fctx.manifest().flags(f))
616
611
617 def fileannotate(self, tmpl, fctx):
612 def fileannotate(self, tmpl, fctx):
618 f = fctx.path()
613 f = fctx.path()
619 n = fctx.filenode()
614 n = fctx.filenode()
620 fl = fctx.filelog()
615 fl = fctx.filelog()
621 parity = paritygen(self.stripecount)
616 parity = paritygen(self.stripecount)
622
617
623 def annotate(**map):
618 def annotate(**map):
624 last = None
619 last = None
625 for f, l in fctx.annotate(follow=True):
620 for f, l in fctx.annotate(follow=True):
626 fnode = f.filenode()
621 fnode = f.filenode()
627 name = self.repo.ui.shortuser(f.user())
622 name = self.repo.ui.shortuser(f.user())
628
623
629 if last != fnode:
624 if last != fnode:
630 last = fnode
625 last = fnode
631
626
632 yield {"parity": parity.next(),
627 yield {"parity": parity.next(),
633 "node": hex(f.node()),
628 "node": hex(f.node()),
634 "rev": f.rev(),
629 "rev": f.rev(),
635 "author": name,
630 "author": name,
636 "file": f.path(),
631 "file": f.path(),
637 "line": l}
632 "line": l}
638
633
639 return tmpl("fileannotate",
634 return tmpl("fileannotate",
640 file=f,
635 file=f,
641 annotate=annotate,
636 annotate=annotate,
642 path=_up(f),
637 path=_up(f),
643 rev=fctx.rev(),
638 rev=fctx.rev(),
644 node=hex(fctx.node()),
639 node=hex(fctx.node()),
645 author=fctx.user(),
640 author=fctx.user(),
646 date=fctx.date(),
641 date=fctx.date(),
647 desc=fctx.description(),
642 desc=fctx.description(),
648 rename=self.renamelink(fl, n),
643 rename=self.renamelink(fl, n),
649 parent=self.siblings(fctx.parents()),
644 parent=self.siblings(fctx.parents()),
650 child=self.siblings(fctx.children()),
645 child=self.siblings(fctx.children()),
651 permissions=fctx.manifest().flags(f))
646 permissions=fctx.manifest().flags(f))
652
647
653 def manifest(self, tmpl, ctx, path):
648 def manifest(self, tmpl, ctx, path):
654 mf = ctx.manifest()
649 mf = ctx.manifest()
655 node = ctx.node()
650 node = ctx.node()
656
651
657 files = {}
652 files = {}
658 parity = paritygen(self.stripecount)
653 parity = paritygen(self.stripecount)
659
654
660 if path and path[-1] != "/":
655 if path and path[-1] != "/":
661 path += "/"
656 path += "/"
662 l = len(path)
657 l = len(path)
663 abspath = "/" + path
658 abspath = "/" + path
664
659
665 for f, n in mf.items():
660 for f, n in mf.items():
666 if f[:l] != path:
661 if f[:l] != path:
667 continue
662 continue
668 remain = f[l:]
663 remain = f[l:]
669 if "/" in remain:
664 if "/" in remain:
670 short = remain[:remain.index("/") + 1] # bleah
665 short = remain[:remain.index("/") + 1] # bleah
671 files[short] = (f, None)
666 files[short] = (f, None)
672 else:
667 else:
673 short = os.path.basename(remain)
668 short = os.path.basename(remain)
674 files[short] = (f, n)
669 files[short] = (f, n)
675
670
676 if not files:
671 if not files:
677 raise ErrorResponse(404, 'Path not found: ' + path)
672 raise ErrorResponse(404, 'Path not found: ' + path)
678
673
679 def filelist(**map):
674 def filelist(**map):
680 fl = files.keys()
675 fl = files.keys()
681 fl.sort()
676 fl.sort()
682 for f in fl:
677 for f in fl:
683 full, fnode = files[f]
678 full, fnode = files[f]
684 if not fnode:
679 if not fnode:
685 continue
680 continue
686
681
687 fctx = ctx.filectx(full)
682 fctx = ctx.filectx(full)
688 yield {"file": full,
683 yield {"file": full,
689 "parity": parity.next(),
684 "parity": parity.next(),
690 "basename": f,
685 "basename": f,
691 "date": fctx.changectx().date(),
686 "date": fctx.changectx().date(),
692 "size": fctx.size(),
687 "size": fctx.size(),
693 "permissions": mf.flags(full)}
688 "permissions": mf.flags(full)}
694
689
695 def dirlist(**map):
690 def dirlist(**map):
696 fl = files.keys()
691 fl = files.keys()
697 fl.sort()
692 fl.sort()
698 for f in fl:
693 for f in fl:
699 full, fnode = files[f]
694 full, fnode = files[f]
700 if fnode:
695 if fnode:
701 continue
696 continue
702
697
703 yield {"parity": parity.next(),
698 yield {"parity": parity.next(),
704 "path": "%s%s" % (abspath, f),
699 "path": "%s%s" % (abspath, f),
705 "basename": f[:-1]}
700 "basename": f[:-1]}
706
701
707 return tmpl("manifest",
702 return tmpl("manifest",
708 rev=ctx.rev(),
703 rev=ctx.rev(),
709 node=hex(node),
704 node=hex(node),
710 path=abspath,
705 path=abspath,
711 up=_up(abspath),
706 up=_up(abspath),
712 upparity=parity.next(),
707 upparity=parity.next(),
713 fentries=filelist,
708 fentries=filelist,
714 dentries=dirlist,
709 dentries=dirlist,
715 archives=self.archivelist(hex(node)),
710 archives=self.archivelist(hex(node)),
716 tags=self.nodetagsdict(node),
711 tags=self.nodetagsdict(node),
717 branches=self.nodebranchdict(ctx))
712 branches=self.nodebranchdict(ctx))
718
713
719 def tags(self, tmpl):
714 def tags(self, tmpl):
720 i = self.repo.tagslist()
715 i = self.repo.tagslist()
721 i.reverse()
716 i.reverse()
722 parity = paritygen(self.stripecount)
717 parity = paritygen(self.stripecount)
723
718
724 def entries(notip=False,limit=0, **map):
719 def entries(notip=False,limit=0, **map):
725 count = 0
720 count = 0
726 for k, n in i:
721 for k, n in i:
727 if notip and k == "tip":
722 if notip and k == "tip":
728 continue
723 continue
729 if limit > 0 and count >= limit:
724 if limit > 0 and count >= limit:
730 continue
725 continue
731 count = count + 1
726 count = count + 1
732 yield {"parity": parity.next(),
727 yield {"parity": parity.next(),
733 "tag": k,
728 "tag": k,
734 "date": self.repo.changectx(n).date(),
729 "date": self.repo.changectx(n).date(),
735 "node": hex(n)}
730 "node": hex(n)}
736
731
737 return tmpl("tags",
732 return tmpl("tags",
738 node=hex(self.repo.changelog.tip()),
733 node=hex(self.repo.changelog.tip()),
739 entries=lambda **x: entries(False,0, **x),
734 entries=lambda **x: entries(False,0, **x),
740 entriesnotip=lambda **x: entries(True,0, **x),
735 entriesnotip=lambda **x: entries(True,0, **x),
741 latestentry=lambda **x: entries(True,1, **x))
736 latestentry=lambda **x: entries(True,1, **x))
742
737
743 def summary(self, tmpl):
738 def summary(self, tmpl):
744 i = self.repo.tagslist()
739 i = self.repo.tagslist()
745 i.reverse()
740 i.reverse()
746
741
747 def tagentries(**map):
742 def tagentries(**map):
748 parity = paritygen(self.stripecount)
743 parity = paritygen(self.stripecount)
749 count = 0
744 count = 0
750 for k, n in i:
745 for k, n in i:
751 if k == "tip": # skip tip
746 if k == "tip": # skip tip
752 continue;
747 continue;
753
748
754 count += 1
749 count += 1
755 if count > 10: # limit to 10 tags
750 if count > 10: # limit to 10 tags
756 break;
751 break;
757
752
758 yield tmpl("tagentry",
753 yield tmpl("tagentry",
759 parity=parity.next(),
754 parity=parity.next(),
760 tag=k,
755 tag=k,
761 node=hex(n),
756 node=hex(n),
762 date=self.repo.changectx(n).date())
757 date=self.repo.changectx(n).date())
763
758
764
759
765 def branches(**map):
760 def branches(**map):
766 parity = paritygen(self.stripecount)
761 parity = paritygen(self.stripecount)
767
762
768 b = self.repo.branchtags()
763 b = self.repo.branchtags()
769 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
764 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
770 l.sort()
765 l.sort()
771
766
772 for r,n,t in l:
767 for r,n,t in l:
773 ctx = self.repo.changectx(n)
768 ctx = self.repo.changectx(n)
774
769
775 yield {'parity': parity.next(),
770 yield {'parity': parity.next(),
776 'branch': t,
771 'branch': t,
777 'node': hex(n),
772 'node': hex(n),
778 'date': ctx.date()}
773 'date': ctx.date()}
779
774
780 def changelist(**map):
775 def changelist(**map):
781 parity = paritygen(self.stripecount, offset=start-end)
776 parity = paritygen(self.stripecount, offset=start-end)
782 l = [] # build a list in forward order for efficiency
777 l = [] # build a list in forward order for efficiency
783 for i in xrange(start, end):
778 for i in xrange(start, end):
784 ctx = self.repo.changectx(i)
779 ctx = self.repo.changectx(i)
785 n = ctx.node()
780 n = ctx.node()
786 hn = hex(n)
781 hn = hex(n)
787
782
788 l.insert(0, tmpl(
783 l.insert(0, tmpl(
789 'shortlogentry',
784 'shortlogentry',
790 parity=parity.next(),
785 parity=parity.next(),
791 author=ctx.user(),
786 author=ctx.user(),
792 desc=ctx.description(),
787 desc=ctx.description(),
793 date=ctx.date(),
788 date=ctx.date(),
794 rev=i,
789 rev=i,
795 node=hn,
790 node=hn,
796 tags=self.nodetagsdict(n),
791 tags=self.nodetagsdict(n),
797 branches=self.nodebranchdict(ctx)))
792 branches=self.nodebranchdict(ctx)))
798
793
799 yield l
794 yield l
800
795
801 cl = self.repo.changelog
796 cl = self.repo.changelog
802 count = cl.count()
797 count = cl.count()
803 start = max(0, count - self.maxchanges)
798 start = max(0, count - self.maxchanges)
804 end = min(count, start + self.maxchanges)
799 end = min(count, start + self.maxchanges)
805
800
806 return tmpl("summary",
801 return tmpl("summary",
807 desc=self.config("web", "description", "unknown"),
802 desc=self.config("web", "description", "unknown"),
808 owner=get_contact(self.config) or "unknown",
803 owner=get_contact(self.config) or "unknown",
809 lastchange=cl.read(cl.tip())[2],
804 lastchange=cl.read(cl.tip())[2],
810 tags=tagentries,
805 tags=tagentries,
811 branches=branches,
806 branches=branches,
812 shortlog=changelist,
807 shortlog=changelist,
813 node=hex(cl.tip()),
808 node=hex(cl.tip()),
814 archives=self.archivelist("tip"))
809 archives=self.archivelist("tip"))
815
810
816 def filediff(self, tmpl, fctx):
811 def filediff(self, tmpl, fctx):
817 n = fctx.node()
812 n = fctx.node()
818 path = fctx.path()
813 path = fctx.path()
819 parents = fctx.parents()
814 parents = fctx.parents()
820 p1 = parents and parents[0].node() or nullid
815 p1 = parents and parents[0].node() or nullid
821
816
822 def diff(**map):
817 def diff(**map):
823 yield self.diff(tmpl, p1, n, [path])
818 yield self.diff(tmpl, p1, n, [path])
824
819
825 return tmpl("filediff",
820 return tmpl("filediff",
826 file=path,
821 file=path,
827 node=hex(n),
822 node=hex(n),
828 rev=fctx.rev(),
823 rev=fctx.rev(),
829 parent=self.siblings(parents),
824 parent=self.siblings(parents),
830 child=self.siblings(fctx.children()),
825 child=self.siblings(fctx.children()),
831 diff=diff)
826 diff=diff)
832
827
833 archive_specs = {
828 archive_specs = {
834 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
829 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
835 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
830 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
836 'zip': ('application/zip', 'zip', '.zip', None),
831 'zip': ('application/zip', 'zip', '.zip', None),
837 }
832 }
838
833
839 def archive(self, tmpl, req, key, type_):
834 def archive(self, tmpl, req, key, type_):
840 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
835 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
841 cnode = self.repo.lookup(key)
836 cnode = self.repo.lookup(key)
842 arch_version = key
837 arch_version = key
843 if cnode == key or key == 'tip':
838 if cnode == key or key == 'tip':
844 arch_version = short(cnode)
839 arch_version = short(cnode)
845 name = "%s-%s" % (reponame, arch_version)
840 name = "%s-%s" % (reponame, arch_version)
846 mimetype, artype, extension, encoding = self.archive_specs[type_]
841 mimetype, artype, extension, encoding = self.archive_specs[type_]
847 headers = [
842 headers = [
848 ('Content-Type', mimetype),
843 ('Content-Type', mimetype),
849 ('Content-Disposition', 'attachment; filename=%s%s' %
844 ('Content-Disposition', 'attachment; filename=%s%s' %
850 (name, extension))
845 (name, extension))
851 ]
846 ]
852 if encoding:
847 if encoding:
853 headers.append(('Content-Encoding', encoding))
848 headers.append(('Content-Encoding', encoding))
854 req.header(headers)
849 req.header(headers)
855 archival.archive(self.repo, req, cnode, artype, prefix=name)
850 archival.archive(self.repo, req, cnode, artype, prefix=name)
856
851
857 # add tags to things
852 # add tags to things
858 # tags -> list of changesets corresponding to tags
853 # tags -> list of changesets corresponding to tags
859 # find tag, changeset, file
854 # find tag, changeset, file
860
855
861 def cleanpath(self, path):
856 def cleanpath(self, path):
862 path = path.lstrip('/')
857 path = path.lstrip('/')
863 return util.canonpath(self.repo.root, '', path)
858 return util.canonpath(self.repo.root, '', path)
864
859
865 def changectx(self, req):
860 def changectx(self, req):
866 if 'node' in req.form:
861 if 'node' in req.form:
867 changeid = req.form['node'][0]
862 changeid = req.form['node'][0]
868 elif 'manifest' in req.form:
863 elif 'manifest' in req.form:
869 changeid = req.form['manifest'][0]
864 changeid = req.form['manifest'][0]
870 else:
865 else:
871 changeid = self.repo.changelog.count() - 1
866 changeid = self.repo.changelog.count() - 1
872
867
873 try:
868 try:
874 ctx = self.repo.changectx(changeid)
869 ctx = self.repo.changectx(changeid)
875 except hg.RepoError:
870 except hg.RepoError:
876 man = self.repo.manifest
871 man = self.repo.manifest
877 mn = man.lookup(changeid)
872 mn = man.lookup(changeid)
878 ctx = self.repo.changectx(man.linkrev(mn))
873 ctx = self.repo.changectx(man.linkrev(mn))
879
874
880 return ctx
875 return ctx
881
876
882 def filectx(self, req):
877 def filectx(self, req):
883 path = self.cleanpath(req.form['file'][0])
878 path = self.cleanpath(req.form['file'][0])
884 if 'node' in req.form:
879 if 'node' in req.form:
885 changeid = req.form['node'][0]
880 changeid = req.form['node'][0]
886 else:
881 else:
887 changeid = req.form['filenode'][0]
882 changeid = req.form['filenode'][0]
888 try:
883 try:
889 ctx = self.repo.changectx(changeid)
884 ctx = self.repo.changectx(changeid)
890 fctx = ctx.filectx(path)
885 fctx = ctx.filectx(path)
891 except hg.RepoError:
886 except hg.RepoError:
892 fctx = self.repo.filectx(path, fileid=changeid)
887 fctx = self.repo.filectx(path, fileid=changeid)
893
888
894 return fctx
889 return fctx
895
890
896 def check_perm(self, req, op, default):
891 def check_perm(self, req, op, default):
897 '''check permission for operation based on user auth.
892 '''check permission for operation based on user auth.
898 return true if op allowed, else false.
893 return true if op allowed, else false.
899 default is policy to use if no config given.'''
894 default is policy to use if no config given.'''
900
895
901 user = req.env.get('REMOTE_USER')
896 user = req.env.get('REMOTE_USER')
902
897
903 deny = self.configlist('web', 'deny_' + op)
898 deny = self.configlist('web', 'deny_' + op)
904 if deny and (not user or deny == ['*'] or user in deny):
899 if deny and (not user or deny == ['*'] or user in deny):
905 return False
900 return False
906
901
907 allow = self.configlist('web', 'allow_' + op)
902 allow = self.configlist('web', 'allow_' + op)
908 return (allow and (allow == ['*'] or user in allow)) or default
903 return (allow and (allow == ['*'] or user in allow)) or default
General Comments 0
You need to be logged in to leave comments. Login now