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