##// END OF EJS Templates
Remove tabs, and trailing whitespace from hgweb.py
Josef "Jeff" Sipek -
r1579:85803ec2 default
parent child Browse files
Show More
@@ -1,1100 +1,1100 b''
1 # hgweb.py - web interface to a mercurial repository
1 # hgweb.py - web interface to a mercurial 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 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005 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, cgi, sys
9 import os, cgi, sys
10 from demandload import demandload
10 from demandload import demandload
11 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
11 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
12 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
12 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
13 demandload(globals(), "mimetypes")
13 demandload(globals(), "mimetypes")
14 from node import *
14 from node import *
15 from i18n import gettext as _
15 from i18n import gettext as _
16
16
17 def templatepath():
17 def templatepath():
18 for f in "templates", "../templates":
18 for f in "templates", "../templates":
19 p = os.path.join(os.path.dirname(__file__), f)
19 p = os.path.join(os.path.dirname(__file__), f)
20 if os.path.isdir(p):
20 if os.path.isdir(p):
21 return p
21 return p
22
22
23 def age(x):
23 def age(x):
24 def plural(t, c):
24 def plural(t, c):
25 if c == 1:
25 if c == 1:
26 return t
26 return t
27 return t + "s"
27 return t + "s"
28 def fmt(t, c):
28 def fmt(t, c):
29 return "%d %s" % (c, plural(t, c))
29 return "%d %s" % (c, plural(t, c))
30
30
31 now = time.time()
31 now = time.time()
32 then = x[0]
32 then = x[0]
33 delta = max(1, int(now - then))
33 delta = max(1, int(now - then))
34
34
35 scales = [["second", 1],
35 scales = [["second", 1],
36 ["minute", 60],
36 ["minute", 60],
37 ["hour", 3600],
37 ["hour", 3600],
38 ["day", 3600 * 24],
38 ["day", 3600 * 24],
39 ["week", 3600 * 24 * 7],
39 ["week", 3600 * 24 * 7],
40 ["month", 3600 * 24 * 30],
40 ["month", 3600 * 24 * 30],
41 ["year", 3600 * 24 * 365]]
41 ["year", 3600 * 24 * 365]]
42
42
43 scales.reverse()
43 scales.reverse()
44
44
45 for t, s in scales:
45 for t, s in scales:
46 n = delta / s
46 n = delta / s
47 if n >= 2 or s == 1:
47 if n >= 2 or s == 1:
48 return fmt(t, n)
48 return fmt(t, n)
49
49
50 def nl2br(text):
50 def nl2br(text):
51 return text.replace('\n', '<br/>\n')
51 return text.replace('\n', '<br/>\n')
52
52
53 def obfuscate(text):
53 def obfuscate(text):
54 return ''.join(['&#%d;' % ord(c) for c in text])
54 return ''.join(['&#%d;' % ord(c) for c in text])
55
55
56 def up(p):
56 def up(p):
57 if p[0] != "/":
57 if p[0] != "/":
58 p = "/" + p
58 p = "/" + p
59 if p[-1] == "/":
59 if p[-1] == "/":
60 p = p[:-1]
60 p = p[:-1]
61 up = os.path.dirname(p)
61 up = os.path.dirname(p)
62 if up == "/":
62 if up == "/":
63 return "/"
63 return "/"
64 return up + "/"
64 return up + "/"
65
65
66 def get_mtime(repo_path):
66 def get_mtime(repo_path):
67 hg_path = os.path.join(repo_path, ".hg")
67 hg_path = os.path.join(repo_path, ".hg")
68 cl_path = os.path.join(hg_path, "00changelog.i")
68 cl_path = os.path.join(hg_path, "00changelog.i")
69 if os.path.exists(os.path.join(cl_path)):
69 if os.path.exists(os.path.join(cl_path)):
70 return os.stat(cl_path).st_mtime
70 return os.stat(cl_path).st_mtime
71 else:
71 else:
72 return os.stat(hg_path).st_mtime
72 return os.stat(hg_path).st_mtime
73
73
74 class hgrequest(object):
74 class hgrequest(object):
75 def __init__(self, inp=None, out=None, env=None):
75 def __init__(self, inp=None, out=None, env=None):
76 self.inp = inp or sys.stdin
76 self.inp = inp or sys.stdin
77 self.out = out or sys.stdout
77 self.out = out or sys.stdout
78 self.env = env or os.environ
78 self.env = env or os.environ
79 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
79 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
80
80
81 def write(self, *things):
81 def write(self, *things):
82 for thing in things:
82 for thing in things:
83 if hasattr(thing, "__iter__"):
83 if hasattr(thing, "__iter__"):
84 for part in thing:
84 for part in thing:
85 self.write(part)
85 self.write(part)
86 else:
86 else:
87 try:
87 try:
88 self.out.write(str(thing))
88 self.out.write(str(thing))
89 except socket.error, inst:
89 except socket.error, inst:
90 if inst[0] != errno.ECONNRESET:
90 if inst[0] != errno.ECONNRESET:
91 raise
91 raise
92
92
93 def header(self, headers=[('Content-type','text/html')]):
93 def header(self, headers=[('Content-type','text/html')]):
94 for header in headers:
94 for header in headers:
95 self.out.write("%s: %s\r\n" % header)
95 self.out.write("%s: %s\r\n" % header)
96 self.out.write("\r\n")
96 self.out.write("\r\n")
97
97
98 def httphdr(self, type, file="", size=0):
98 def httphdr(self, type, file="", size=0):
99
99
100 headers = [('Content-type', type)]
100 headers = [('Content-type', type)]
101 if file:
101 if file:
102 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
102 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
103 if size > 0:
103 if size > 0:
104 headers.append(('Content-length', str(size)))
104 headers.append(('Content-length', str(size)))
105 self.header(headers)
105 self.header(headers)
106
106
107 class templater(object):
107 class templater(object):
108 def __init__(self, mapfile, filters={}, defaults={}):
108 def __init__(self, mapfile, filters={}, defaults={}):
109 self.cache = {}
109 self.cache = {}
110 self.map = {}
110 self.map = {}
111 self.base = os.path.dirname(mapfile)
111 self.base = os.path.dirname(mapfile)
112 self.filters = filters
112 self.filters = filters
113 self.defaults = defaults
113 self.defaults = defaults
114
114
115 for l in file(mapfile):
115 for l in file(mapfile):
116 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
116 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
117 if m:
117 if m:
118 self.cache[m.group(1)] = m.group(2)
118 self.cache[m.group(1)] = m.group(2)
119 else:
119 else:
120 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
120 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
121 if m:
121 if m:
122 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
122 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
123 else:
123 else:
124 raise LookupError(_("unknown map entry '%s'") % l)
124 raise LookupError(_("unknown map entry '%s'") % l)
125
125
126 def __call__(self, t, **map):
126 def __call__(self, t, **map):
127 m = self.defaults.copy()
127 m = self.defaults.copy()
128 m.update(map)
128 m.update(map)
129 try:
129 try:
130 tmpl = self.cache[t]
130 tmpl = self.cache[t]
131 except KeyError:
131 except KeyError:
132 tmpl = self.cache[t] = file(self.map[t]).read()
132 tmpl = self.cache[t] = file(self.map[t]).read()
133 return self.template(tmpl, self.filters, **m)
133 return self.template(tmpl, self.filters, **m)
134
134
135 def template(self, tmpl, filters={}, **map):
135 def template(self, tmpl, filters={}, **map):
136 while tmpl:
136 while tmpl:
137 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
137 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
138 if m:
138 if m:
139 yield tmpl[:m.start(0)]
139 yield tmpl[:m.start(0)]
140 v = map.get(m.group(1), "")
140 v = map.get(m.group(1), "")
141 v = callable(v) and v(**map) or v
141 v = callable(v) and v(**map) or v
142
142
143 format = m.group(2)
143 format = m.group(2)
144 fl = m.group(4)
144 fl = m.group(4)
145
145
146 if format:
146 if format:
147 q = v.__iter__
147 q = v.__iter__
148 for i in q():
148 for i in q():
149 lm = map.copy()
149 lm = map.copy()
150 lm.update(i)
150 lm.update(i)
151 yield self(format[1:], **lm)
151 yield self(format[1:], **lm)
152
152
153 v = ""
153 v = ""
154
154
155 elif fl:
155 elif fl:
156 for f in fl.split("|")[1:]:
156 for f in fl.split("|")[1:]:
157 v = filters[f](v)
157 v = filters[f](v)
158
158
159 yield v
159 yield v
160 tmpl = tmpl[m.end(0):]
160 tmpl = tmpl[m.end(0):]
161 else:
161 else:
162 yield tmpl
162 yield tmpl
163 return
163 return
164
164
165 common_filters = {
165 common_filters = {
166 "escape": cgi.escape,
166 "escape": cgi.escape,
167 "strip": lambda x: x.strip(),
167 "strip": lambda x: x.strip(),
168 "age": age,
168 "age": age,
169 "date": lambda x: util.datestr(x),
169 "date": lambda x: util.datestr(x),
170 "addbreaks": nl2br,
170 "addbreaks": nl2br,
171 "obfuscate": obfuscate,
171 "obfuscate": obfuscate,
172 "short": (lambda x: x[:12]),
172 "short": (lambda x: x[:12]),
173 "firstline": (lambda x: x.splitlines(1)[0]),
173 "firstline": (lambda x: x.splitlines(1)[0]),
174 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
174 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
175 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
175 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
176 }
176 }
177
177
178 class hgweb(object):
178 class hgweb(object):
179 def __init__(self, repo, name=None):
179 def __init__(self, repo, name=None):
180 if type(repo) == type(""):
180 if type(repo) == type(""):
181 self.repo = hg.repository(ui.ui(), repo)
181 self.repo = hg.repository(ui.ui(), repo)
182 else:
182 else:
183 self.repo = repo
183 self.repo = repo
184
184
185 self.mtime = -1
185 self.mtime = -1
186 self.reponame = name
186 self.reponame = name
187 self.archives = 'zip', 'gz', 'bz2'
187 self.archives = 'zip', 'gz', 'bz2'
188
188
189 def refresh(self):
189 def refresh(self):
190 mtime = get_mtime(self.repo.root)
190 mtime = get_mtime(self.repo.root)
191 if mtime != self.mtime:
191 if mtime != self.mtime:
192 self.mtime = mtime
192 self.mtime = mtime
193 self.repo = hg.repository(self.repo.ui, self.repo.root)
193 self.repo = hg.repository(self.repo.ui, self.repo.root)
194 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
194 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
195 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
195 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
196 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
196 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
197
197
198 def archivelist(self, nodeid):
198 def archivelist(self, nodeid):
199 for i in self.archives:
199 for i in self.archives:
200 if self.repo.ui.configbool("web", "allow" + i, False):
200 if self.repo.ui.configbool("web", "allow" + i, False):
201 yield {"type" : i, "node" : nodeid}
201 yield {"type" : i, "node" : nodeid}
202
202
203 def listfiles(self, files, mf):
203 def listfiles(self, files, mf):
204 for f in files[:self.maxfiles]:
204 for f in files[:self.maxfiles]:
205 yield self.t("filenodelink", node=hex(mf[f]), file=f)
205 yield self.t("filenodelink", node=hex(mf[f]), file=f)
206 if len(files) > self.maxfiles:
206 if len(files) > self.maxfiles:
207 yield self.t("fileellipses")
207 yield self.t("fileellipses")
208
208
209 def listfilediffs(self, files, changeset):
209 def listfilediffs(self, files, changeset):
210 for f in files[:self.maxfiles]:
210 for f in files[:self.maxfiles]:
211 yield self.t("filedifflink", node=hex(changeset), file=f)
211 yield self.t("filedifflink", node=hex(changeset), file=f)
212 if len(files) > self.maxfiles:
212 if len(files) > self.maxfiles:
213 yield self.t("fileellipses")
213 yield self.t("fileellipses")
214
214
215 def parents(self, node, parents=[], rev=None, hide=False, **args):
215 def parents(self, node, parents=[], rev=None, hide=False, **args):
216 if not rev:
216 if not rev:
217 rev = lambda x: ""
217 rev = lambda x: ""
218 parents = [p for p in parents if p != nullid]
218 parents = [p for p in parents if p != nullid]
219 if hide and len(parents) == 1 and rev(parents[0]) == rev(node) - 1:
219 if hide and len(parents) == 1 and rev(parents[0]) == rev(node) - 1:
220 return
220 return
221 for p in parents:
221 for p in parents:
222 yield dict(node=hex(p), rev=rev(p), **args)
222 yield dict(node=hex(p), rev=rev(p), **args)
223
223
224 def showtag(self, t1, node=nullid, **args):
224 def showtag(self, t1, node=nullid, **args):
225 for t in self.repo.nodetags(node):
225 for t in self.repo.nodetags(node):
226 yield self.t(t1, tag=t, **args)
226 yield self.t(t1, tag=t, **args)
227
227
228 def diff(self, node1, node2, files):
228 def diff(self, node1, node2, files):
229 def filterfiles(list, files):
229 def filterfiles(list, files):
230 l = [x for x in list if x in files]
230 l = [x for x in list if x in files]
231
231
232 for f in files:
232 for f in files:
233 if f[-1] != os.sep:
233 if f[-1] != os.sep:
234 f += os.sep
234 f += os.sep
235 l += [x for x in list if x.startswith(f)]
235 l += [x for x in list if x.startswith(f)]
236 return l
236 return l
237
237
238 parity = [0]
238 parity = [0]
239 def diffblock(diff, f, fn):
239 def diffblock(diff, f, fn):
240 yield self.t("diffblock",
240 yield self.t("diffblock",
241 lines=prettyprintlines(diff),
241 lines=prettyprintlines(diff),
242 parity=parity[0],
242 parity=parity[0],
243 file=f,
243 file=f,
244 filenode=hex(fn or nullid))
244 filenode=hex(fn or nullid))
245 parity[0] = 1 - parity[0]
245 parity[0] = 1 - parity[0]
246
246
247 def prettyprintlines(diff):
247 def prettyprintlines(diff):
248 for l in diff.splitlines(1):
248 for l in diff.splitlines(1):
249 if l.startswith('+'):
249 if l.startswith('+'):
250 yield self.t("difflineplus", line=l)
250 yield self.t("difflineplus", line=l)
251 elif l.startswith('-'):
251 elif l.startswith('-'):
252 yield self.t("difflineminus", line=l)
252 yield self.t("difflineminus", line=l)
253 elif l.startswith('@'):
253 elif l.startswith('@'):
254 yield self.t("difflineat", line=l)
254 yield self.t("difflineat", line=l)
255 else:
255 else:
256 yield self.t("diffline", line=l)
256 yield self.t("diffline", line=l)
257
257
258 r = self.repo
258 r = self.repo
259 cl = r.changelog
259 cl = r.changelog
260 mf = r.manifest
260 mf = r.manifest
261 change1 = cl.read(node1)
261 change1 = cl.read(node1)
262 change2 = cl.read(node2)
262 change2 = cl.read(node2)
263 mmap1 = mf.read(change1[0])
263 mmap1 = mf.read(change1[0])
264 mmap2 = mf.read(change2[0])
264 mmap2 = mf.read(change2[0])
265 date1 = util.datestr(change1[2])
265 date1 = util.datestr(change1[2])
266 date2 = util.datestr(change2[2])
266 date2 = util.datestr(change2[2])
267
267
268 c, a, d, u = r.changes(node1, node2)
268 c, a, d, u = r.changes(node1, node2)
269 if files:
269 if files:
270 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
270 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
271
271
272 for f in c:
272 for f in c:
273 to = r.file(f).read(mmap1[f])
273 to = r.file(f).read(mmap1[f])
274 tn = r.file(f).read(mmap2[f])
274 tn = r.file(f).read(mmap2[f])
275 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
275 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
276 for f in a:
276 for f in a:
277 to = None
277 to = None
278 tn = r.file(f).read(mmap2[f])
278 tn = r.file(f).read(mmap2[f])
279 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
279 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
280 for f in d:
280 for f in d:
281 to = r.file(f).read(mmap1[f])
281 to = r.file(f).read(mmap1[f])
282 tn = None
282 tn = None
283 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
283 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
284
284
285 def changelog(self, pos):
285 def changelog(self, pos):
286 def changenav(**map):
286 def changenav(**map):
287 def seq(factor=1):
287 def seq(factor=1):
288 yield 1 * factor
288 yield 1 * factor
289 yield 3 * factor
289 yield 3 * factor
290 #yield 5 * factor
290 #yield 5 * factor
291 for f in seq(factor * 10):
291 for f in seq(factor * 10):
292 yield f
292 yield f
293
293
294 l = []
294 l = []
295 for f in seq():
295 for f in seq():
296 if f < self.maxchanges / 2:
296 if f < self.maxchanges / 2:
297 continue
297 continue
298 if f > count:
298 if f > count:
299 break
299 break
300 r = "%d" % f
300 r = "%d" % f
301 if pos + f < count:
301 if pos + f < count:
302 l.append(("+" + r, pos + f))
302 l.append(("+" + r, pos + f))
303 if pos - f >= 0:
303 if pos - f >= 0:
304 l.insert(0, ("-" + r, pos - f))
304 l.insert(0, ("-" + r, pos - f))
305
305
306 yield {"rev": 0, "label": "(0)"}
306 yield {"rev": 0, "label": "(0)"}
307
307
308 for label, rev in l:
308 for label, rev in l:
309 yield {"label": label, "rev": rev}
309 yield {"label": label, "rev": rev}
310
310
311 yield {"label": "tip", "rev": "tip"}
311 yield {"label": "tip", "rev": "tip"}
312
312
313 def changelist(**map):
313 def changelist(**map):
314 parity = (start - end) & 1
314 parity = (start - end) & 1
315 cl = self.repo.changelog
315 cl = self.repo.changelog
316 l = [] # build a list in forward order for efficiency
316 l = [] # build a list in forward order for efficiency
317 for i in range(start, end):
317 for i in range(start, end):
318 n = cl.node(i)
318 n = cl.node(i)
319 changes = cl.read(n)
319 changes = cl.read(n)
320 hn = hex(n)
320 hn = hex(n)
321
321
322 l.insert(0, {"parity": parity,
322 l.insert(0, {"parity": parity,
323 "author": changes[1],
323 "author": changes[1],
324 "parent": self.parents(n, cl.parents(n), cl.rev,
324 "parent": self.parents(n, cl.parents(n), cl.rev,
325 hide=True),
325 hide=True),
326 "changelogtag": self.showtag("changelogtag",n),
326 "changelogtag": self.showtag("changelogtag",n),
327 "manifest": hex(changes[0]),
327 "manifest": hex(changes[0]),
328 "desc": changes[4],
328 "desc": changes[4],
329 "date": changes[2],
329 "date": changes[2],
330 "files": self.listfilediffs(changes[3], n),
330 "files": self.listfilediffs(changes[3], n),
331 "rev": i,
331 "rev": i,
332 "node": hn})
332 "node": hn})
333 parity = 1 - parity
333 parity = 1 - parity
334
334
335 for e in l:
335 for e in l:
336 yield e
336 yield e
337
337
338 cl = self.repo.changelog
338 cl = self.repo.changelog
339 mf = cl.read(cl.tip())[0]
339 mf = cl.read(cl.tip())[0]
340 count = cl.count()
340 count = cl.count()
341 start = max(0, pos - self.maxchanges + 1)
341 start = max(0, pos - self.maxchanges + 1)
342 end = min(count, start + self.maxchanges)
342 end = min(count, start + self.maxchanges)
343 pos = end - 1
343 pos = end - 1
344
344
345 yield self.t('changelog',
345 yield self.t('changelog',
346 changenav=changenav,
346 changenav=changenav,
347 manifest=hex(mf),
347 manifest=hex(mf),
348 rev=pos, changesets=count, entries=changelist)
348 rev=pos, changesets=count, entries=changelist)
349
349
350 def search(self, query):
350 def search(self, query):
351
351
352 def changelist(**map):
352 def changelist(**map):
353 cl = self.repo.changelog
353 cl = self.repo.changelog
354 count = 0
354 count = 0
355 qw = query.lower().split()
355 qw = query.lower().split()
356
356
357 def revgen():
357 def revgen():
358 for i in range(cl.count() - 1, 0, -100):
358 for i in range(cl.count() - 1, 0, -100):
359 l = []
359 l = []
360 for j in range(max(0, i - 100), i):
360 for j in range(max(0, i - 100), i):
361 n = cl.node(j)
361 n = cl.node(j)
362 changes = cl.read(n)
362 changes = cl.read(n)
363 l.append((n, j, changes))
363 l.append((n, j, changes))
364 l.reverse()
364 l.reverse()
365 for e in l:
365 for e in l:
366 yield e
366 yield e
367
367
368 for n, i, changes in revgen():
368 for n, i, changes in revgen():
369 miss = 0
369 miss = 0
370 for q in qw:
370 for q in qw:
371 if not (q in changes[1].lower() or
371 if not (q in changes[1].lower() or
372 q in changes[4].lower() or
372 q in changes[4].lower() or
373 q in " ".join(changes[3][:20]).lower()):
373 q in " ".join(changes[3][:20]).lower()):
374 miss = 1
374 miss = 1
375 break
375 break
376 if miss:
376 if miss:
377 continue
377 continue
378
378
379 count += 1
379 count += 1
380 hn = hex(n)
380 hn = hex(n)
381
381
382 yield self.t('searchentry',
382 yield self.t('searchentry',
383 parity=count & 1,
383 parity=count & 1,
384 author=changes[1],
384 author=changes[1],
385 parent=self.parents(n, cl.parents(n), cl.rev),
385 parent=self.parents(n, cl.parents(n), cl.rev),
386 changelogtag=self.showtag("changelogtag",n),
386 changelogtag=self.showtag("changelogtag",n),
387 manifest=hex(changes[0]),
387 manifest=hex(changes[0]),
388 desc=changes[4],
388 desc=changes[4],
389 date=changes[2],
389 date=changes[2],
390 files=self.listfilediffs(changes[3], n),
390 files=self.listfilediffs(changes[3], n),
391 rev=i,
391 rev=i,
392 node=hn)
392 node=hn)
393
393
394 if count >= self.maxchanges:
394 if count >= self.maxchanges:
395 break
395 break
396
396
397 cl = self.repo.changelog
397 cl = self.repo.changelog
398 mf = cl.read(cl.tip())[0]
398 mf = cl.read(cl.tip())[0]
399
399
400 yield self.t('search',
400 yield self.t('search',
401 query=query,
401 query=query,
402 manifest=hex(mf),
402 manifest=hex(mf),
403 entries=changelist)
403 entries=changelist)
404
404
405 def changeset(self, nodeid):
405 def changeset(self, nodeid):
406 cl = self.repo.changelog
406 cl = self.repo.changelog
407 n = self.repo.lookup(nodeid)
407 n = self.repo.lookup(nodeid)
408 nodeid = hex(n)
408 nodeid = hex(n)
409 changes = cl.read(n)
409 changes = cl.read(n)
410 p1 = cl.parents(n)[0]
410 p1 = cl.parents(n)[0]
411
411
412 files = []
412 files = []
413 mf = self.repo.manifest.read(changes[0])
413 mf = self.repo.manifest.read(changes[0])
414 for f in changes[3]:
414 for f in changes[3]:
415 files.append(self.t("filenodelink",
415 files.append(self.t("filenodelink",
416 filenode=hex(mf.get(f, nullid)), file=f))
416 filenode=hex(mf.get(f, nullid)), file=f))
417
417
418 def diff(**map):
418 def diff(**map):
419 yield self.diff(p1, n, None)
419 yield self.diff(p1, n, None)
420
420
421 yield self.t('changeset',
421 yield self.t('changeset',
422 diff=diff,
422 diff=diff,
423 rev=cl.rev(n),
423 rev=cl.rev(n),
424 node=nodeid,
424 node=nodeid,
425 parent=self.parents(n, cl.parents(n), cl.rev),
425 parent=self.parents(n, cl.parents(n), cl.rev),
426 changesettag=self.showtag("changesettag",n),
426 changesettag=self.showtag("changesettag",n),
427 manifest=hex(changes[0]),
427 manifest=hex(changes[0]),
428 author=changes[1],
428 author=changes[1],
429 desc=changes[4],
429 desc=changes[4],
430 date=changes[2],
430 date=changes[2],
431 files=files,
431 files=files,
432 archives=self.archivelist(nodeid))
432 archives=self.archivelist(nodeid))
433
433
434 def filelog(self, f, filenode):
434 def filelog(self, f, filenode):
435 cl = self.repo.changelog
435 cl = self.repo.changelog
436 fl = self.repo.file(f)
436 fl = self.repo.file(f)
437 filenode = hex(fl.lookup(filenode))
437 filenode = hex(fl.lookup(filenode))
438 count = fl.count()
438 count = fl.count()
439
439
440 def entries(**map):
440 def entries(**map):
441 l = []
441 l = []
442 parity = (count - 1) & 1
442 parity = (count - 1) & 1
443
443
444 for i in range(count):
444 for i in range(count):
445 n = fl.node(i)
445 n = fl.node(i)
446 lr = fl.linkrev(n)
446 lr = fl.linkrev(n)
447 cn = cl.node(lr)
447 cn = cl.node(lr)
448 cs = cl.read(cl.node(lr))
448 cs = cl.read(cl.node(lr))
449
449
450 l.insert(0, {"parity": parity,
450 l.insert(0, {"parity": parity,
451 "filenode": hex(n),
451 "filenode": hex(n),
452 "filerev": i,
452 "filerev": i,
453 "file": f,
453 "file": f,
454 "node": hex(cn),
454 "node": hex(cn),
455 "author": cs[1],
455 "author": cs[1],
456 "date": cs[2],
456 "date": cs[2],
457 "parent": self.parents(n, fl.parents(n),
457 "parent": self.parents(n, fl.parents(n),
458 fl.rev, file=f),
458 fl.rev, file=f),
459 "desc": cs[4]})
459 "desc": cs[4]})
460 parity = 1 - parity
460 parity = 1 - parity
461
461
462 for e in l:
462 for e in l:
463 yield e
463 yield e
464
464
465 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
465 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
466
466
467 def filerevision(self, f, node):
467 def filerevision(self, f, node):
468 fl = self.repo.file(f)
468 fl = self.repo.file(f)
469 n = fl.lookup(node)
469 n = fl.lookup(node)
470 node = hex(n)
470 node = hex(n)
471 text = fl.read(n)
471 text = fl.read(n)
472 changerev = fl.linkrev(n)
472 changerev = fl.linkrev(n)
473 cl = self.repo.changelog
473 cl = self.repo.changelog
474 cn = cl.node(changerev)
474 cn = cl.node(changerev)
475 cs = cl.read(cn)
475 cs = cl.read(cn)
476 mfn = cs[0]
476 mfn = cs[0]
477
477
478 mt = mimetypes.guess_type(f)[0]
478 mt = mimetypes.guess_type(f)[0]
479 rawtext = text
479 rawtext = text
480 if util.binary(text):
480 if util.binary(text):
481 text = "(binary:%s)" % mt
481 text = "(binary:%s)" % mt
482
482
483 def lines():
483 def lines():
484 for l, t in enumerate(text.splitlines(1)):
484 for l, t in enumerate(text.splitlines(1)):
485 yield {"line": t,
485 yield {"line": t,
486 "linenumber": "% 6d" % (l + 1),
486 "linenumber": "% 6d" % (l + 1),
487 "parity": l & 1}
487 "parity": l & 1}
488
488
489 yield self.t("filerevision",
489 yield self.t("filerevision",
490 file=f,
490 file=f,
491 filenode=node,
491 filenode=node,
492 path=up(f),
492 path=up(f),
493 text=lines(),
493 text=lines(),
494 raw=rawtext,
494 raw=rawtext,
495 mimetype=mt,
495 mimetype=mt,
496 rev=changerev,
496 rev=changerev,
497 node=hex(cn),
497 node=hex(cn),
498 manifest=hex(mfn),
498 manifest=hex(mfn),
499 author=cs[1],
499 author=cs[1],
500 date=cs[2],
500 date=cs[2],
501 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
501 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
502 permissions=self.repo.manifest.readflags(mfn)[f])
502 permissions=self.repo.manifest.readflags(mfn)[f])
503
503
504 def fileannotate(self, f, node):
504 def fileannotate(self, f, node):
505 bcache = {}
505 bcache = {}
506 ncache = {}
506 ncache = {}
507 fl = self.repo.file(f)
507 fl = self.repo.file(f)
508 n = fl.lookup(node)
508 n = fl.lookup(node)
509 node = hex(n)
509 node = hex(n)
510 changerev = fl.linkrev(n)
510 changerev = fl.linkrev(n)
511
511
512 cl = self.repo.changelog
512 cl = self.repo.changelog
513 cn = cl.node(changerev)
513 cn = cl.node(changerev)
514 cs = cl.read(cn)
514 cs = cl.read(cn)
515 mfn = cs[0]
515 mfn = cs[0]
516
516
517 def annotate(**map):
517 def annotate(**map):
518 parity = 1
518 parity = 1
519 last = None
519 last = None
520 for r, l in fl.annotate(n):
520 for r, l in fl.annotate(n):
521 try:
521 try:
522 cnode = ncache[r]
522 cnode = ncache[r]
523 except KeyError:
523 except KeyError:
524 cnode = ncache[r] = self.repo.changelog.node(r)
524 cnode = ncache[r] = self.repo.changelog.node(r)
525
525
526 try:
526 try:
527 name = bcache[r]
527 name = bcache[r]
528 except KeyError:
528 except KeyError:
529 cl = self.repo.changelog.read(cnode)
529 cl = self.repo.changelog.read(cnode)
530 bcache[r] = name = self.repo.ui.shortuser(cl[1])
530 bcache[r] = name = self.repo.ui.shortuser(cl[1])
531
531
532 if last != cnode:
532 if last != cnode:
533 parity = 1 - parity
533 parity = 1 - parity
534 last = cnode
534 last = cnode
535
535
536 yield {"parity": parity,
536 yield {"parity": parity,
537 "node": hex(cnode),
537 "node": hex(cnode),
538 "rev": r,
538 "rev": r,
539 "author": name,
539 "author": name,
540 "file": f,
540 "file": f,
541 "line": l}
541 "line": l}
542
542
543 yield self.t("fileannotate",
543 yield self.t("fileannotate",
544 file=f,
544 file=f,
545 filenode=node,
545 filenode=node,
546 annotate=annotate,
546 annotate=annotate,
547 path=up(f),
547 path=up(f),
548 rev=changerev,
548 rev=changerev,
549 node=hex(cn),
549 node=hex(cn),
550 manifest=hex(mfn),
550 manifest=hex(mfn),
551 author=cs[1],
551 author=cs[1],
552 date=cs[2],
552 date=cs[2],
553 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
553 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
554 permissions=self.repo.manifest.readflags(mfn)[f])
554 permissions=self.repo.manifest.readflags(mfn)[f])
555
555
556 def manifest(self, mnode, path):
556 def manifest(self, mnode, path):
557 man = self.repo.manifest
557 man = self.repo.manifest
558 mn = man.lookup(mnode)
558 mn = man.lookup(mnode)
559 mnode = hex(mn)
559 mnode = hex(mn)
560 mf = man.read(mn)
560 mf = man.read(mn)
561 rev = man.rev(mn)
561 rev = man.rev(mn)
562 node = self.repo.changelog.node(rev)
562 node = self.repo.changelog.node(rev)
563 mff = man.readflags(mn)
563 mff = man.readflags(mn)
564
564
565 files = {}
565 files = {}
566
566
567 p = path[1:]
567 p = path[1:]
568 l = len(p)
568 l = len(p)
569
569
570 for f,n in mf.items():
570 for f,n in mf.items():
571 if f[:l] != p:
571 if f[:l] != p:
572 continue
572 continue
573 remain = f[l:]
573 remain = f[l:]
574 if "/" in remain:
574 if "/" in remain:
575 short = remain[:remain.find("/") + 1] # bleah
575 short = remain[:remain.find("/") + 1] # bleah
576 files[short] = (f, None)
576 files[short] = (f, None)
577 else:
577 else:
578 short = os.path.basename(remain)
578 short = os.path.basename(remain)
579 files[short] = (f, n)
579 files[short] = (f, n)
580
580
581 def filelist(**map):
581 def filelist(**map):
582 parity = 0
582 parity = 0
583 fl = files.keys()
583 fl = files.keys()
584 fl.sort()
584 fl.sort()
585 for f in fl:
585 for f in fl:
586 full, fnode = files[f]
586 full, fnode = files[f]
587 if not fnode:
587 if not fnode:
588 continue
588 continue
589
589
590 yield {"file": full,
590 yield {"file": full,
591 "manifest": mnode,
591 "manifest": mnode,
592 "filenode": hex(fnode),
592 "filenode": hex(fnode),
593 "parity": parity,
593 "parity": parity,
594 "basename": f,
594 "basename": f,
595 "permissions": mff[full]}
595 "permissions": mff[full]}
596 parity = 1 - parity
596 parity = 1 - parity
597
597
598 def dirlist(**map):
598 def dirlist(**map):
599 parity = 0
599 parity = 0
600 fl = files.keys()
600 fl = files.keys()
601 fl.sort()
601 fl.sort()
602 for f in fl:
602 for f in fl:
603 full, fnode = files[f]
603 full, fnode = files[f]
604 if fnode:
604 if fnode:
605 continue
605 continue
606
606
607 yield {"parity": parity,
607 yield {"parity": parity,
608 "path": os.path.join(path, f),
608 "path": os.path.join(path, f),
609 "manifest": mnode,
609 "manifest": mnode,
610 "basename": f[:-1]}
610 "basename": f[:-1]}
611 parity = 1 - parity
611 parity = 1 - parity
612
612
613 yield self.t("manifest",
613 yield self.t("manifest",
614 manifest=mnode,
614 manifest=mnode,
615 rev=rev,
615 rev=rev,
616 node=hex(node),
616 node=hex(node),
617 path=path,
617 path=path,
618 up=up(path),
618 up=up(path),
619 fentries=filelist,
619 fentries=filelist,
620 dentries=dirlist,
620 dentries=dirlist,
621 archives=self.archivelist(hex(node)))
621 archives=self.archivelist(hex(node)))
622
622
623 def tags(self):
623 def tags(self):
624 cl = self.repo.changelog
624 cl = self.repo.changelog
625 mf = cl.read(cl.tip())[0]
625 mf = cl.read(cl.tip())[0]
626
626
627 i = self.repo.tagslist()
627 i = self.repo.tagslist()
628 i.reverse()
628 i.reverse()
629
629
630 def entries(**map):
630 def entries(**map):
631 parity = 0
631 parity = 0
632 for k,n in i:
632 for k,n in i:
633 yield {"parity": parity,
633 yield {"parity": parity,
634 "tag": k,
634 "tag": k,
635 "tagmanifest": hex(cl.read(n)[0]),
635 "tagmanifest": hex(cl.read(n)[0]),
636 "date": cl.read(n)[2],
636 "date": cl.read(n)[2],
637 "node": hex(n)}
637 "node": hex(n)}
638 parity = 1 - parity
638 parity = 1 - parity
639
639
640 yield self.t("tags",
640 yield self.t("tags",
641 manifest=hex(mf),
641 manifest=hex(mf),
642 entries=entries)
642 entries=entries)
643
643
644 def summary(self):
644 def summary(self):
645 cl = self.repo.changelog
645 cl = self.repo.changelog
646 mf = cl.read(cl.tip())[0]
646 mf = cl.read(cl.tip())[0]
647
647
648 i = self.repo.tagslist()
648 i = self.repo.tagslist()
649 i.reverse()
649 i.reverse()
650
650
651 def tagentries(**map):
651 def tagentries(**map):
652 parity = 0
652 parity = 0
653 count = 0
653 count = 0
654 for k,n in i:
654 for k,n in i:
655 if k == "tip": # skip tip
655 if k == "tip": # skip tip
656 continue;
656 continue;
657
657
658 count += 1
658 count += 1
659 if count > 10: # limit to 10 tags
659 if count > 10: # limit to 10 tags
660 break;
660 break;
661
661
662 c = cl.read(n)
662 c = cl.read(n)
663 m = c[0]
663 m = c[0]
664 t = c[2]
664 t = c[2]
665
665
666 yield self.t("tagentry",
666 yield self.t("tagentry",
667 parity = parity,
667 parity = parity,
668 tag = k,
668 tag = k,
669 node = hex(n),
669 node = hex(n),
670 date = t,
670 date = t,
671 tagmanifest = hex(m))
671 tagmanifest = hex(m))
672 parity = 1 - parity
672 parity = 1 - parity
673
673
674 def changelist(**map):
674 def changelist(**map):
675 parity = 0
675 parity = 0
676 cl = self.repo.changelog
676 cl = self.repo.changelog
677 l = [] # build a list in forward order for efficiency
677 l = [] # build a list in forward order for efficiency
678 for i in range(start, end):
678 for i in range(start, end):
679 n = cl.node(i)
679 n = cl.node(i)
680 changes = cl.read(n)
680 changes = cl.read(n)
681 hn = hex(n)
681 hn = hex(n)
682 t = changes[2]
682 t = changes[2]
683
683
684 l.insert(0, self.t(
684 l.insert(0, self.t(
685 'shortlogentry',
685 'shortlogentry',
686 parity = parity,
686 parity = parity,
687 author = changes[1],
687 author = changes[1],
688 manifest = hex(changes[0]),
688 manifest = hex(changes[0]),
689 desc = changes[4],
689 desc = changes[4],
690 date = t,
690 date = t,
691 rev = i,
691 rev = i,
692 node = hn))
692 node = hn))
693 parity = 1 - parity
693 parity = 1 - parity
694
694
695 yield l
695 yield l
696
696
697 cl = self.repo.changelog
697 cl = self.repo.changelog
698 mf = cl.read(cl.tip())[0]
698 mf = cl.read(cl.tip())[0]
699 count = cl.count()
699 count = cl.count()
700 start = max(0, count - self.maxchanges)
700 start = max(0, count - self.maxchanges)
701 end = min(count, start + self.maxchanges)
701 end = min(count, start + self.maxchanges)
702 pos = end - 1
702 pos = end - 1
703
703
704 yield self.t("summary",
704 yield self.t("summary",
705 desc = self.repo.ui.config("web", "description", "unknown"),
705 desc = self.repo.ui.config("web", "description", "unknown"),
706 owner = (self.repo.ui.config("ui", "username") or # preferred
706 owner = (self.repo.ui.config("ui", "username") or # preferred
707 self.repo.ui.config("web", "contact") or # deprecated
707 self.repo.ui.config("web", "contact") or # deprecated
708 self.repo.ui.config("web", "author", "unknown")), # also
708 self.repo.ui.config("web", "author", "unknown")), # also
709 lastchange = (0, 0), # FIXME
709 lastchange = (0, 0), # FIXME
710 manifest = hex(mf),
710 manifest = hex(mf),
711 tags = tagentries,
711 tags = tagentries,
712 shortlog = changelist)
712 shortlog = changelist)
713
713
714 def filediff(self, file, changeset):
714 def filediff(self, file, changeset):
715 cl = self.repo.changelog
715 cl = self.repo.changelog
716 n = self.repo.lookup(changeset)
716 n = self.repo.lookup(changeset)
717 changeset = hex(n)
717 changeset = hex(n)
718 p1 = cl.parents(n)[0]
718 p1 = cl.parents(n)[0]
719 cs = cl.read(n)
719 cs = cl.read(n)
720 mf = self.repo.manifest.read(cs[0])
720 mf = self.repo.manifest.read(cs[0])
721
721
722 def diff(**map):
722 def diff(**map):
723 yield self.diff(p1, n, file)
723 yield self.diff(p1, n, file)
724
724
725 yield self.t("filediff",
725 yield self.t("filediff",
726 file=file,
726 file=file,
727 filenode=hex(mf.get(file, nullid)),
727 filenode=hex(mf.get(file, nullid)),
728 node=changeset,
728 node=changeset,
729 rev=self.repo.changelog.rev(n),
729 rev=self.repo.changelog.rev(n),
730 parent=self.parents(n, cl.parents(n), cl.rev),
730 parent=self.parents(n, cl.parents(n), cl.rev),
731 diff=diff)
731 diff=diff)
732
732
733 def archive(self, req, cnode, type):
733 def archive(self, req, cnode, type):
734 cs = self.repo.changelog.read(cnode)
734 cs = self.repo.changelog.read(cnode)
735 mnode = cs[0]
735 mnode = cs[0]
736 mf = self.repo.manifest.read(mnode)
736 mf = self.repo.manifest.read(mnode)
737 rev = self.repo.manifest.rev(mnode)
737 rev = self.repo.manifest.rev(mnode)
738 reponame = re.sub(r"\W+", "-", self.reponame)
738 reponame = re.sub(r"\W+", "-", self.reponame)
739 name = "%s-%s/" % (reponame, short(cnode))
739 name = "%s-%s/" % (reponame, short(cnode))
740
740
741 files = mf.keys()
741 files = mf.keys()
742 files.sort()
742 files.sort()
743
743
744 if type == 'zip':
744 if type == 'zip':
745 tmp = tempfile.mkstemp()[1]
745 tmp = tempfile.mkstemp()[1]
746 try:
746 try:
747 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
747 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
748
748
749 for f in files:
749 for f in files:
750 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
750 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
751 zf.close()
751 zf.close()
752
752
753 f = open(tmp, 'r')
753 f = open(tmp, 'r')
754 req.httphdr('application/zip', name[:-1] + '.zip',
754 req.httphdr('application/zip', name[:-1] + '.zip',
755 os.path.getsize(tmp))
755 os.path.getsize(tmp))
756 req.write(f.read())
756 req.write(f.read())
757 f.close()
757 f.close()
758 finally:
758 finally:
759 os.unlink(tmp)
759 os.unlink(tmp)
760
760
761 else:
761 else:
762 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
762 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
763 mff = self.repo.manifest.readflags(mnode)
763 mff = self.repo.manifest.readflags(mnode)
764 mtime = int(time.time())
764 mtime = int(time.time())
765
765
766 if type == "gz":
766 if type == "gz":
767 encoding = "gzip"
767 encoding = "gzip"
768 else:
768 else:
769 encoding = "x-bzip2"
769 encoding = "x-bzip2"
770 req.header([('Content-type', 'application/x-tar'),
770 req.header([('Content-type', 'application/x-tar'),
771 ('Content-disposition', 'attachment; filename=%s%s%s' %
771 ('Content-disposition', 'attachment; filename=%s%s%s' %
772 (name[:-1], '.tar.', type)),
772 (name[:-1], '.tar.', type)),
773 ('Content-encoding', encoding)])
773 ('Content-encoding', encoding)])
774 for fname in files:
774 for fname in files:
775 rcont = self.repo.file(fname).read(mf[fname])
775 rcont = self.repo.file(fname).read(mf[fname])
776 finfo = tarfile.TarInfo(name + fname)
776 finfo = tarfile.TarInfo(name + fname)
777 finfo.mtime = mtime
777 finfo.mtime = mtime
778 finfo.size = len(rcont)
778 finfo.size = len(rcont)
779 finfo.mode = mff[fname] and 0755 or 0644
779 finfo.mode = mff[fname] and 0755 or 0644
780 tf.addfile(finfo, StringIO.StringIO(rcont))
780 tf.addfile(finfo, StringIO.StringIO(rcont))
781 tf.close()
781 tf.close()
782
782
783 # add tags to things
783 # add tags to things
784 # tags -> list of changesets corresponding to tags
784 # tags -> list of changesets corresponding to tags
785 # find tag, changeset, file
785 # find tag, changeset, file
786
786
787 def run(self, req=hgrequest()):
787 def run(self, req=hgrequest()):
788 def header(**map):
788 def header(**map):
789 yield self.t("header", **map)
789 yield self.t("header", **map)
790
790
791 def footer(**map):
791 def footer(**map):
792 yield self.t("footer", **map)
792 yield self.t("footer", **map)
793
793
794 def expand_form(form):
794 def expand_form(form):
795 shortcuts = {
795 shortcuts = {
796 'cl': [('cmd', ['changelog']), ('rev', None)],
796 'cl': [('cmd', ['changelog']), ('rev', None)],
797 'cs': [('cmd', ['changeset']), ('node', None)],
797 'cs': [('cmd', ['changeset']), ('node', None)],
798 'f': [('cmd', ['file']), ('filenode', None)],
798 'f': [('cmd', ['file']), ('filenode', None)],
799 'fl': [('cmd', ['filelog']), ('filenode', None)],
799 'fl': [('cmd', ['filelog']), ('filenode', None)],
800 'fd': [('cmd', ['filediff']), ('node', None)],
800 'fd': [('cmd', ['filediff']), ('node', None)],
801 'fa': [('cmd', ['annotate']), ('filenode', None)],
801 'fa': [('cmd', ['annotate']), ('filenode', None)],
802 'mf': [('cmd', ['manifest']), ('manifest', None)],
802 'mf': [('cmd', ['manifest']), ('manifest', None)],
803 'ca': [('cmd', ['archive']), ('node', None)],
803 'ca': [('cmd', ['archive']), ('node', None)],
804 'tags': [('cmd', ['tags'])],
804 'tags': [('cmd', ['tags'])],
805 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
805 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
806 }
806 }
807
807
808 for k in shortcuts.iterkeys():
808 for k in shortcuts.iterkeys():
809 if form.has_key(k):
809 if form.has_key(k):
810 for name, value in shortcuts[k]:
810 for name, value in shortcuts[k]:
811 if value is None:
811 if value is None:
812 value = form[k]
812 value = form[k]
813 form[name] = value
813 form[name] = value
814 del form[k]
814 del form[k]
815
815
816 self.refresh()
816 self.refresh()
817
817
818 expand_form(req.form)
818 expand_form(req.form)
819
819
820 t = self.repo.ui.config("web", "templates", templatepath())
820 t = self.repo.ui.config("web", "templates", templatepath())
821 m = os.path.join(t, "map")
821 m = os.path.join(t, "map")
822 style = self.repo.ui.config("web", "style", "")
822 style = self.repo.ui.config("web", "style", "")
823 if req.form.has_key('style'):
823 if req.form.has_key('style'):
824 style = req.form['style'][0]
824 style = req.form['style'][0]
825 if style:
825 if style:
826 b = os.path.basename("map-" + style)
826 b = os.path.basename("map-" + style)
827 p = os.path.join(t, b)
827 p = os.path.join(t, b)
828 if os.path.isfile(p):
828 if os.path.isfile(p):
829 m = p
829 m = p
830
830
831 port = req.env["SERVER_PORT"]
831 port = req.env["SERVER_PORT"]
832 port = port != "80" and (":" + port) or ""
832 port = port != "80" and (":" + port) or ""
833 uri = req.env["REQUEST_URI"]
833 uri = req.env["REQUEST_URI"]
834 if "?" in uri:
834 if "?" in uri:
835 uri = uri.split("?")[0]
835 uri = uri.split("?")[0]
836 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
836 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
837 if not self.reponame:
837 if not self.reponame:
838 self.reponame = (self.repo.ui.config("web", "name")
838 self.reponame = (self.repo.ui.config("web", "name")
839 or uri.strip('/') or self.repo.root)
839 or uri.strip('/') or self.repo.root)
840
840
841 self.t = templater(m, common_filters,
841 self.t = templater(m, common_filters,
842 {"url": url,
842 {"url": url,
843 "repo": self.reponame,
843 "repo": self.reponame,
844 "header": header,
844 "header": header,
845 "footer": footer,
845 "footer": footer,
846 })
846 })
847
847
848 if not req.form.has_key('cmd'):
848 if not req.form.has_key('cmd'):
849 req.form['cmd'] = [self.t.cache['default'],]
849 req.form['cmd'] = [self.t.cache['default'],]
850
850
851 if req.form['cmd'][0] == 'changelog':
851 if req.form['cmd'][0] == 'changelog':
852 c = self.repo.changelog.count() - 1
852 c = self.repo.changelog.count() - 1
853 hi = c
853 hi = c
854 if req.form.has_key('rev'):
854 if req.form.has_key('rev'):
855 hi = req.form['rev'][0]
855 hi = req.form['rev'][0]
856 try:
856 try:
857 hi = self.repo.changelog.rev(self.repo.lookup(hi))
857 hi = self.repo.changelog.rev(self.repo.lookup(hi))
858 except hg.RepoError:
858 except hg.RepoError:
859 req.write(self.search(hi))
859 req.write(self.search(hi))
860 return
860 return
861
861
862 req.write(self.changelog(hi))
862 req.write(self.changelog(hi))
863
863
864 elif req.form['cmd'][0] == 'changeset':
864 elif req.form['cmd'][0] == 'changeset':
865 req.write(self.changeset(req.form['node'][0]))
865 req.write(self.changeset(req.form['node'][0]))
866
866
867 elif req.form['cmd'][0] == 'manifest':
867 elif req.form['cmd'][0] == 'manifest':
868 req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
868 req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
869
869
870 elif req.form['cmd'][0] == 'tags':
870 elif req.form['cmd'][0] == 'tags':
871 req.write(self.tags())
871 req.write(self.tags())
872
872
873 elif req.form['cmd'][0] == 'summary':
873 elif req.form['cmd'][0] == 'summary':
874 req.write(self.summary())
874 req.write(self.summary())
875
875
876 elif req.form['cmd'][0] == 'filediff':
876 elif req.form['cmd'][0] == 'filediff':
877 req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
877 req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
878
878
879 elif req.form['cmd'][0] == 'file':
879 elif req.form['cmd'][0] == 'file':
880 req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
880 req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
881
881
882 elif req.form['cmd'][0] == 'annotate':
882 elif req.form['cmd'][0] == 'annotate':
883 req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
883 req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
884
884
885 elif req.form['cmd'][0] == 'filelog':
885 elif req.form['cmd'][0] == 'filelog':
886 req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
886 req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
887
887
888 elif req.form['cmd'][0] == 'heads':
888 elif req.form['cmd'][0] == 'heads':
889 req.httphdr("application/mercurial-0.1")
889 req.httphdr("application/mercurial-0.1")
890 h = self.repo.heads()
890 h = self.repo.heads()
891 req.write(" ".join(map(hex, h)) + "\n")
891 req.write(" ".join(map(hex, h)) + "\n")
892
892
893 elif req.form['cmd'][0] == 'branches':
893 elif req.form['cmd'][0] == 'branches':
894 req.httphdr("application/mercurial-0.1")
894 req.httphdr("application/mercurial-0.1")
895 nodes = []
895 nodes = []
896 if req.form.has_key('nodes'):
896 if req.form.has_key('nodes'):
897 nodes = map(bin, req.form['nodes'][0].split(" "))
897 nodes = map(bin, req.form['nodes'][0].split(" "))
898 for b in self.repo.branches(nodes):
898 for b in self.repo.branches(nodes):
899 req.write(" ".join(map(hex, b)) + "\n")
899 req.write(" ".join(map(hex, b)) + "\n")
900
900
901 elif req.form['cmd'][0] == 'between':
901 elif req.form['cmd'][0] == 'between':
902 req.httphdr("application/mercurial-0.1")
902 req.httphdr("application/mercurial-0.1")
903 nodes = []
903 nodes = []
904 if req.form.has_key('pairs'):
904 if req.form.has_key('pairs'):
905 pairs = [map(bin, p.split("-"))
905 pairs = [map(bin, p.split("-"))
906 for p in req.form['pairs'][0].split(" ")]
906 for p in req.form['pairs'][0].split(" ")]
907 for b in self.repo.between(pairs):
907 for b in self.repo.between(pairs):
908 req.write(" ".join(map(hex, b)) + "\n")
908 req.write(" ".join(map(hex, b)) + "\n")
909
909
910 elif req.form['cmd'][0] == 'changegroup':
910 elif req.form['cmd'][0] == 'changegroup':
911 req.httphdr("application/mercurial-0.1")
911 req.httphdr("application/mercurial-0.1")
912 nodes = []
912 nodes = []
913 if not self.allowpull:
913 if not self.allowpull:
914 return
914 return
915
915
916 if req.form.has_key('roots'):
916 if req.form.has_key('roots'):
917 nodes = map(bin, req.form['roots'][0].split(" "))
917 nodes = map(bin, req.form['roots'][0].split(" "))
918
918
919 z = zlib.compressobj()
919 z = zlib.compressobj()
920 f = self.repo.changegroup(nodes)
920 f = self.repo.changegroup(nodes)
921 while 1:
921 while 1:
922 chunk = f.read(4096)
922 chunk = f.read(4096)
923 if not chunk:
923 if not chunk:
924 break
924 break
925 req.write(z.compress(chunk))
925 req.write(z.compress(chunk))
926
926
927 req.write(z.flush())
927 req.write(z.flush())
928
928
929 elif req.form['cmd'][0] == 'archive':
929 elif req.form['cmd'][0] == 'archive':
930 changeset = self.repo.lookup(req.form['node'][0])
930 changeset = self.repo.lookup(req.form['node'][0])
931 type = req.form['type'][0]
931 type = req.form['type'][0]
932 if (type in self.archives and
932 if (type in self.archives and
933 self.repo.ui.configbool("web", "allow" + type, False)):
933 self.repo.ui.configbool("web", "allow" + type, False)):
934 self.archive(req, changeset, type)
934 self.archive(req, changeset, type)
935 return
935 return
936
936
937 req.write(self.t("error"))
937 req.write(self.t("error"))
938
938
939 else:
939 else:
940 req.write(self.t("error"))
940 req.write(self.t("error"))
941
941
942 def create_server(repo):
942 def create_server(repo):
943
943
944 def openlog(opt, default):
944 def openlog(opt, default):
945 if opt and opt != '-':
945 if opt and opt != '-':
946 return open(opt, 'w')
946 return open(opt, 'w')
947 return default
947 return default
948
948
949 address = repo.ui.config("web", "address", "")
949 address = repo.ui.config("web", "address", "")
950 port = int(repo.ui.config("web", "port", 8000))
950 port = int(repo.ui.config("web", "port", 8000))
951 use_ipv6 = repo.ui.configbool("web", "ipv6")
951 use_ipv6 = repo.ui.configbool("web", "ipv6")
952 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
952 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
953 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
953 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
954
954
955 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
955 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
956 address_family = getattr(socket, 'AF_INET6', None)
956 address_family = getattr(socket, 'AF_INET6', None)
957
957
958 def __init__(self, *args, **kwargs):
958 def __init__(self, *args, **kwargs):
959 if self.address_family is None:
959 if self.address_family is None:
960 raise hg.RepoError(_('IPv6 not available on this system'))
960 raise hg.RepoError(_('IPv6 not available on this system'))
961 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
961 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
962
962
963 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
963 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
964 def log_error(self, format, *args):
964 def log_error(self, format, *args):
965 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
965 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
966 self.log_date_time_string(),
966 self.log_date_time_string(),
967 format % args))
967 format % args))
968
968
969 def log_message(self, format, *args):
969 def log_message(self, format, *args):
970 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
970 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
971 self.log_date_time_string(),
971 self.log_date_time_string(),
972 format % args))
972 format % args))
973
973
974 def do_POST(self):
974 def do_POST(self):
975 try:
975 try:
976 self.do_hgweb()
976 self.do_hgweb()
977 except socket.error, inst:
977 except socket.error, inst:
978 if inst[0] != errno.EPIPE:
978 if inst[0] != errno.EPIPE:
979 raise
979 raise
980
980
981 def do_GET(self):
981 def do_GET(self):
982 self.do_POST()
982 self.do_POST()
983
983
984 def do_hgweb(self):
984 def do_hgweb(self):
985 query = ""
985 query = ""
986 p = self.path.find("?")
986 p = self.path.find("?")
987 if p:
987 if p:
988 query = self.path[p + 1:]
988 query = self.path[p + 1:]
989 query = query.replace('+', ' ')
989 query = query.replace('+', ' ')
990
990
991 env = {}
991 env = {}
992 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
992 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
993 env['REQUEST_METHOD'] = self.command
993 env['REQUEST_METHOD'] = self.command
994 env['SERVER_NAME'] = self.server.server_name
994 env['SERVER_NAME'] = self.server.server_name
995 env['SERVER_PORT'] = str(self.server.server_port)
995 env['SERVER_PORT'] = str(self.server.server_port)
996 env['REQUEST_URI'] = "/"
996 env['REQUEST_URI'] = "/"
997 if query:
997 if query:
998 env['QUERY_STRING'] = query
998 env['QUERY_STRING'] = query
999 host = self.address_string()
999 host = self.address_string()
1000 if host != self.client_address[0]:
1000 if host != self.client_address[0]:
1001 env['REMOTE_HOST'] = host
1001 env['REMOTE_HOST'] = host
1002 env['REMOTE_ADDR'] = self.client_address[0]
1002 env['REMOTE_ADDR'] = self.client_address[0]
1003
1003
1004 if self.headers.typeheader is None:
1004 if self.headers.typeheader is None:
1005 env['CONTENT_TYPE'] = self.headers.type
1005 env['CONTENT_TYPE'] = self.headers.type
1006 else:
1006 else:
1007 env['CONTENT_TYPE'] = self.headers.typeheader
1007 env['CONTENT_TYPE'] = self.headers.typeheader
1008 length = self.headers.getheader('content-length')
1008 length = self.headers.getheader('content-length')
1009 if length:
1009 if length:
1010 env['CONTENT_LENGTH'] = length
1010 env['CONTENT_LENGTH'] = length
1011 accept = []
1011 accept = []
1012 for line in self.headers.getallmatchingheaders('accept'):
1012 for line in self.headers.getallmatchingheaders('accept'):
1013 if line[:1] in "\t\n\r ":
1013 if line[:1] in "\t\n\r ":
1014 accept.append(line.strip())
1014 accept.append(line.strip())
1015 else:
1015 else:
1016 accept = accept + line[7:].split(',')
1016 accept = accept + line[7:].split(',')
1017 env['HTTP_ACCEPT'] = ','.join(accept)
1017 env['HTTP_ACCEPT'] = ','.join(accept)
1018
1018
1019 req = hgrequest(self.rfile, self.wfile, env)
1019 req = hgrequest(self.rfile, self.wfile, env)
1020 self.send_response(200, "Script output follows")
1020 self.send_response(200, "Script output follows")
1021 hg.run(req)
1021 hg.run(req)
1022
1022
1023 hg = hgweb(repo)
1023 hg = hgweb(repo)
1024 if use_ipv6:
1024 if use_ipv6:
1025 return IPv6HTTPServer((address, port), hgwebhandler)
1025 return IPv6HTTPServer((address, port), hgwebhandler)
1026 else:
1026 else:
1027 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
1027 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
1028
1028
1029 # This is a stopgap
1029 # This is a stopgap
1030 class hgwebdir(object):
1030 class hgwebdir(object):
1031 def __init__(self, config):
1031 def __init__(self, config):
1032 def cleannames(items):
1032 def cleannames(items):
1033 return [(name.strip('/'), path) for name, path in items]
1033 return [(name.strip('/'), path) for name, path in items]
1034
1034
1035 if type(config) == type([]):
1035 if type(config) == type([]):
1036 self.repos = cleannames(config)
1036 self.repos = cleannames(config)
1037 elif type(config) == type({}):
1037 elif type(config) == type({}):
1038 self.repos = cleannames(config.items())
1038 self.repos = cleannames(config.items())
1039 self.repos.sort()
1039 self.repos.sort()
1040 else:
1040 else:
1041 cp = ConfigParser.SafeConfigParser()
1041 cp = ConfigParser.SafeConfigParser()
1042 cp.read(config)
1042 cp.read(config)
1043 self.repos = cleannames(cp.items("paths"))
1043 self.repos = cleannames(cp.items("paths"))
1044 self.repos.sort()
1044 self.repos.sort()
1045
1045
1046 def run(self, req=hgrequest()):
1046 def run(self, req=hgrequest()):
1047 def header(**map):
1047 def header(**map):
1048 yield tmpl("header", **map)
1048 yield tmpl("header", **map)
1049
1049
1050 def footer(**map):
1050 def footer(**map):
1051 yield tmpl("footer", **map)
1051 yield tmpl("footer", **map)
1052
1052
1053 m = os.path.join(templatepath(), "map")
1053 m = os.path.join(templatepath(), "map")
1054 tmpl = templater(m, common_filters,
1054 tmpl = templater(m, common_filters,
1055 {"header": header, "footer": footer})
1055 {"header": header, "footer": footer})
1056
1056
1057 def entries(**map):
1057 def entries(**map):
1058 parity = 0
1058 parity = 0
1059 for name, path in self.repos:
1059 for name, path in self.repos:
1060 u = ui.ui()
1060 u = ui.ui()
1061 try:
1061 try:
1062 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
1062 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
1063 except IOError:
1063 except IOError:
1064 pass
1064 pass
1065 get = u.config
1065 get = u.config
1066
1066
1067 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
1067 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
1068 .replace("//", "/"))
1068 .replace("//", "/"))
1069
1069
1070 # update time with local timezone
1070 # update time with local timezone
1071 try:
1071 try:
1072 d = (get_mtime(path), util.makedate()[1])
1072 d = (get_mtime(path), util.makedate()[1])
1073 except OSError:
1073 except OSError:
1074 continue
1074 continue
1075
1075
1076 yield dict(contact=(get("ui", "username") or # preferred
1076 yield dict(contact=(get("ui", "username") or # preferred
1077 get("web", "contact") or # deprecated
1077 get("web", "contact") or # deprecated
1078 get("web", "author", "unknown")), # also
1078 get("web", "author", "unknown")), # also
1079 name=get("web", "name", name),
1079 name=get("web", "name", name),
1080 url=url,
1080 url=url,
1081 parity=parity,
1081 parity=parity,
1082 shortdesc=get("web", "description", "unknown"),
1082 shortdesc=get("web", "description", "unknown"),
1083 lastupdate=d)
1083 lastupdate=d)
1084
1084
1085 parity = 1 - parity
1085 parity = 1 - parity
1086
1086
1087 virtual = req.env.get("PATH_INFO", "").strip('/')
1087 virtual = req.env.get("PATH_INFO", "").strip('/')
1088 if virtual:
1088 if virtual:
1089 real = dict(self.repos).get(virtual)
1089 real = dict(self.repos).get(virtual)
1090 if real:
1090 if real:
1091 try:
1091 try:
1092 hgweb(real).run(req)
1092 hgweb(real).run(req)
1093 except IOError, inst:
1093 except IOError, inst:
1094 req.write(tmpl("error", error=inst.strerror))
1094 req.write(tmpl("error", error=inst.strerror))
1095 except hg.RepoError, inst:
1095 except hg.RepoError, inst:
1096 req.write(tmpl("error", error=str(inst)))
1096 req.write(tmpl("error", error=str(inst)))
1097 else:
1097 else:
1098 req.write(tmpl("notfound", repo=virtual))
1098 req.write(tmpl("notfound", repo=virtual))
1099 else:
1099 else:
1100 req.write(tmpl("index", entries=entries))
1100 req.write(tmpl("index", entries=entries))
General Comments 0
You need to be logged in to leave comments. Login now