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