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