##// END OF EJS Templates
Add globals to templater/fixup RSS...
mpm@selenic.com -
r601:8865eb8a default
parent child Browse files
Show More
@@ -1,782 +1,763 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, time, re, difflib, sys, zlib
9 import os, cgi, time, re, difflib, sys, zlib
10 from mercurial.hg import *
10 from mercurial.hg import *
11 from mercurial.ui import *
11 from mercurial.ui import *
12
12
13 def templatepath():
13 def templatepath():
14 for f in "templates", "../templates":
14 for f in "templates", "../templates":
15 p = os.path.join(os.path.dirname(__file__), f)
15 p = os.path.join(os.path.dirname(__file__), f)
16 if os.path.isdir(p): return p
16 if os.path.isdir(p): return p
17
17
18 def age(t):
18 def age(t):
19 def plural(t, c):
19 def plural(t, c):
20 if c == 1: return t
20 if c == 1: return t
21 return t + "s"
21 return t + "s"
22 def fmt(t, c):
22 def fmt(t, c):
23 return "%d %s" % (c, plural(t, c))
23 return "%d %s" % (c, plural(t, c))
24
24
25 now = time.time()
25 now = time.time()
26 delta = max(1, int(now - t))
26 delta = max(1, int(now - t))
27
27
28 scales = [["second", 1],
28 scales = [["second", 1],
29 ["minute", 60],
29 ["minute", 60],
30 ["hour", 3600],
30 ["hour", 3600],
31 ["day", 3600 * 24],
31 ["day", 3600 * 24],
32 ["week", 3600 * 24 * 7],
32 ["week", 3600 * 24 * 7],
33 ["month", 3600 * 24 * 30],
33 ["month", 3600 * 24 * 30],
34 ["year", 3600 * 24 * 365]]
34 ["year", 3600 * 24 * 365]]
35
35
36 scales.reverse()
36 scales.reverse()
37
37
38 for t, s in scales:
38 for t, s in scales:
39 n = delta / s
39 n = delta / s
40 if n >= 2 or s == 1: return fmt(t, n)
40 if n >= 2 or s == 1: return fmt(t, n)
41
41
42 def nl2br(text):
42 def nl2br(text):
43 return text.replace('\n', '<br/>\n')
43 return text.replace('\n', '<br/>\n')
44
44
45 def obfuscate(text):
45 def obfuscate(text):
46 return ''.join([ '&#%d;' % ord(c) for c in text ])
46 return ''.join([ '&#%d;' % ord(c) for c in text ])
47
47
48 def up(p):
48 def up(p):
49 if p[0] != "/": p = "/" + p
49 if p[0] != "/": p = "/" + p
50 if p[-1] == "/": p = p[:-1]
50 if p[-1] == "/": p = p[:-1]
51 up = os.path.dirname(p)
51 up = os.path.dirname(p)
52 if up == "/":
52 if up == "/":
53 return "/"
53 return "/"
54 return up + "/"
54 return up + "/"
55
55
56 def httphdr(type):
56 def httphdr(type):
57 sys.stdout.write('Content-type: %s\n\n' % type)
57 sys.stdout.write('Content-type: %s\n\n' % type)
58
58
59 def write(*things):
59 def write(*things):
60 for thing in things:
60 for thing in things:
61 if hasattr(thing, "__iter__"):
61 if hasattr(thing, "__iter__"):
62 for part in thing:
62 for part in thing:
63 write(part)
63 write(part)
64 else:
64 else:
65 sys.stdout.write(str(thing))
65 sys.stdout.write(str(thing))
66
66
67 def template(tmpl, filters = {}, **map):
67 def template(tmpl, filters = {}, **map):
68 while tmpl:
68 while tmpl:
69 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl)
69 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl)
70 if m:
70 if m:
71 yield tmpl[:m.start(0)]
71 yield tmpl[:m.start(0)]
72 v = map.get(m.group(1), "")
72 v = map.get(m.group(1), "")
73 v = callable(v) and v() or v
73 v = callable(v) and v() or v
74
74
75 fl = m.group(2)
75 fl = m.group(2)
76 if fl:
76 if fl:
77 for f in fl.split("|")[1:]:
77 for f in fl.split("|")[1:]:
78 v = filters[f](v)
78 v = filters[f](v)
79
79
80 yield v
80 yield v
81 tmpl = tmpl[m.end(0):]
81 tmpl = tmpl[m.end(0):]
82 else:
82 else:
83 yield tmpl
83 yield tmpl
84 return
84 return
85
85
86 class templater:
86 class templater:
87 def __init__(self, mapfile, filters = {}):
87 def __init__(self, mapfile, filters = {}, defaults = {}):
88 self.cache = {}
88 self.cache = {}
89 self.map = {}
89 self.map = {}
90 self.base = os.path.dirname(mapfile)
90 self.base = os.path.dirname(mapfile)
91 self.filters = filters
91 self.filters = filters
92 self.defaults = defaults
92
93
93 for l in file(mapfile):
94 for l in file(mapfile):
94 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
95 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
95 if m:
96 if m:
96 self.cache[m.group(1)] = m.group(2)
97 self.cache[m.group(1)] = m.group(2)
97 else:
98 else:
98 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
99 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
99 if m:
100 if m:
100 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
101 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
101 else:
102 else:
102 raise "unknown map entry '%s'" % l
103 raise "unknown map entry '%s'" % l
103
104
104 def __call__(self, t, **map):
105 def __call__(self, t, **map):
106 m = self.defaults.copy()
107 m.update(map)
105 try:
108 try:
106 tmpl = self.cache[t]
109 tmpl = self.cache[t]
107 except KeyError:
110 except KeyError:
108 tmpl = self.cache[t] = file(self.map[t]).read()
111 tmpl = self.cache[t] = file(self.map[t]).read()
109 return template(tmpl, self.filters, **map)
112 return template(tmpl, self.filters, **m)
110
113
111 def rfc822date(x):
114 def rfc822date(x):
112 return strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
115 return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(x))
113
116
114 class hgweb:
117 class hgweb:
115 maxchanges = 10
118 maxchanges = 10
116 maxfiles = 10
119 maxfiles = 10
117
120
118 def __init__(self, path, name, templates = ""):
121 def __init__(self, path, name, templates = ""):
119 self.templates = templates or templatepath()
122 self.templates = templates or templatepath()
120 self.reponame = name
123 self.reponame = name
121 self.path = path
124 self.path = path
122 self.mtime = -1
125 self.mtime = -1
123 self.viewonly = 0
126 self.viewonly = 0
124
127
125 self.filters = {
128 self.filters = {
126 "escape": cgi.escape,
129 "escape": cgi.escape,
127 "age": age,
130 "age": age,
128 "date": (lambda x: time.asctime(time.gmtime(x))),
131 "date": (lambda x: time.asctime(time.gmtime(x))),
129 "addbreaks": nl2br,
132 "addbreaks": nl2br,
130 "obfuscate": obfuscate,
133 "obfuscate": obfuscate,
131 "short": (lambda x: x[:12]),
134 "short": (lambda x: x[:12]),
132 "firstline": (lambda x: x.splitlines(1)[0]),
135 "firstline": (lambda x: x.splitlines(1)[0]),
133 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
136 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
134 "rfc822date": rfc822date,
137 "rfc822date": rfc822date,
135 }
138 }
136
139
137 def refresh(self):
140 def refresh(self):
138 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i"))
141 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i"))
139 if s.st_mtime != self.mtime:
142 if s.st_mtime != self.mtime:
140 self.mtime = s.st_mtime
143 self.mtime = s.st_mtime
141 self.repo = repository(ui(), self.path)
144 self.repo = repository(ui(), self.path)
142
145
143 def date(self, cs):
146 def date(self, cs):
144 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
147 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
145
148
146 def listfiles(self, files, mf):
149 def listfiles(self, files, mf):
147 for f in files[:self.maxfiles]:
150 for f in files[:self.maxfiles]:
148 yield self.t("filenodelink", node = hex(mf[f]), file = f)
151 yield self.t("filenodelink", node = hex(mf[f]), file = f)
149 if len(files) > self.maxfiles:
152 if len(files) > self.maxfiles:
150 yield self.t("fileellipses")
153 yield self.t("fileellipses")
151
154
152 def listfilediffs(self, files, changeset):
155 def listfilediffs(self, files, changeset):
153 for f in files[:self.maxfiles]:
156 for f in files[:self.maxfiles]:
154 yield self.t("filedifflink", node = hex(changeset), file = f)
157 yield self.t("filedifflink", node = hex(changeset), file = f)
155 if len(files) > self.maxfiles:
158 if len(files) > self.maxfiles:
156 yield self.t("fileellipses")
159 yield self.t("fileellipses")
157
160
158 def parents(self, t1, nodes=[], rev=None,**args):
161 def parents(self, t1, nodes=[], rev=None,**args):
159 if not rev: rev = lambda x: ""
162 if not rev: rev = lambda x: ""
160 for node in nodes:
163 for node in nodes:
161 if node != nullid:
164 if node != nullid:
162 yield self.t(t1, node = hex(node), rev = rev(node), **args)
165 yield self.t(t1, node = hex(node), rev = rev(node), **args)
163
166
164 def showtag(self, t1, node=nullid, **args):
167 def showtag(self, t1, node=nullid, **args):
165 for t in self.repo.nodetags(node):
168 for t in self.repo.nodetags(node):
166 yield self.t(t1, tag = t, **args)
169 yield self.t(t1, tag = t, **args)
167
170
168 def diff(self, node1, node2, files):
171 def diff(self, node1, node2, files):
169 def filterfiles(list, files):
172 def filterfiles(list, files):
170 l = [ x for x in list if x in files ]
173 l = [ x for x in list if x in files ]
171
174
172 for f in files:
175 for f in files:
173 if f[-1] != os.sep: f += os.sep
176 if f[-1] != os.sep: f += os.sep
174 l += [ x for x in list if x.startswith(f) ]
177 l += [ x for x in list if x.startswith(f) ]
175 return l
178 return l
176
179
177 parity = [0]
180 parity = [0]
178 def diffblock(diff, f, fn):
181 def diffblock(diff, f, fn):
179 yield self.t("diffblock",
182 yield self.t("diffblock",
180 lines = prettyprintlines(diff),
183 lines = prettyprintlines(diff),
181 parity = parity[0],
184 parity = parity[0],
182 file = f,
185 file = f,
183 filenode = hex(fn or nullid))
186 filenode = hex(fn or nullid))
184 parity[0] = 1 - parity[0]
187 parity[0] = 1 - parity[0]
185
188
186 def prettyprintlines(diff):
189 def prettyprintlines(diff):
187 for l in diff.splitlines(1):
190 for l in diff.splitlines(1):
188 if l.startswith('+'):
191 if l.startswith('+'):
189 yield self.t("difflineplus", line = l)
192 yield self.t("difflineplus", line = l)
190 elif l.startswith('-'):
193 elif l.startswith('-'):
191 yield self.t("difflineminus", line = l)
194 yield self.t("difflineminus", line = l)
192 elif l.startswith('@'):
195 elif l.startswith('@'):
193 yield self.t("difflineat", line = l)
196 yield self.t("difflineat", line = l)
194 else:
197 else:
195 yield self.t("diffline", line = l)
198 yield self.t("diffline", line = l)
196
199
197 r = self.repo
200 r = self.repo
198 cl = r.changelog
201 cl = r.changelog
199 mf = r.manifest
202 mf = r.manifest
200 change1 = cl.read(node1)
203 change1 = cl.read(node1)
201 change2 = cl.read(node2)
204 change2 = cl.read(node2)
202 mmap1 = mf.read(change1[0])
205 mmap1 = mf.read(change1[0])
203 mmap2 = mf.read(change2[0])
206 mmap2 = mf.read(change2[0])
204 date1 = self.date(change1)
207 date1 = self.date(change1)
205 date2 = self.date(change2)
208 date2 = self.date(change2)
206
209
207 c, a, d, u = r.changes(node1, node2)
210 c, a, d, u = r.changes(node1, node2)
208 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
211 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
209
212
210 for f in c:
213 for f in c:
211 to = r.file(f).read(mmap1[f])
214 to = r.file(f).read(mmap1[f])
212 tn = r.file(f).read(mmap2[f])
215 tn = r.file(f).read(mmap2[f])
213 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
216 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
214 for f in a:
217 for f in a:
215 to = None
218 to = None
216 tn = r.file(f).read(mmap2[f])
219 tn = r.file(f).read(mmap2[f])
217 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
220 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
218 for f in d:
221 for f in d:
219 to = r.file(f).read(mmap1[f])
222 to = r.file(f).read(mmap1[f])
220 tn = None
223 tn = None
221 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
224 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
222
225
223 def header(self):
226 def header(self):
224 port = os.environ["SERVER_PORT"]
227 yield self.t("header")
225 port = port != "80" and (":" + port) or ""
226 self.url = "http://%s%s%s" % \
227 (os.environ["SERVER_NAME"], port, os.environ["REQUEST_URI"])
228
229 yield self.t("header", repo = self.reponame, url = self.url)
230
228
231 def footer(self):
229 def footer(self):
232 yield self.t("footer", repo = self.reponame)
230 yield self.t("footer")
233
231
234 def changelog(self, pos):
232 def changelog(self, pos):
235 def changenav():
233 def changenav():
236 def seq(factor = 1):
234 def seq(factor = 1):
237 yield 1 * factor
235 yield 1 * factor
238 yield 3 * factor
236 yield 3 * factor
239 #yield 5 * factor
237 #yield 5 * factor
240 for f in seq(factor * 10):
238 for f in seq(factor * 10):
241 yield f
239 yield f
242
240
243 l = []
241 l = []
244 for f in seq():
242 for f in seq():
245 if f < self.maxchanges / 2: continue
243 if f < self.maxchanges / 2: continue
246 if f > count: break
244 if f > count: break
247 r = "%d" % f
245 r = "%d" % f
248 if pos + f < count: l.append(("+" + r, pos + f))
246 if pos + f < count: l.append(("+" + r, pos + f))
249 if pos - f >= 0: l.insert(0, ("-" + r, pos - f))
247 if pos - f >= 0: l.insert(0, ("-" + r, pos - f))
250
248
251 yield self.t("naventry", rev = 0, label="(0)")
249 yield self.t("naventry", rev = 0, label="(0)")
252
250
253 for label, rev in l:
251 for label, rev in l:
254 yield self.t("naventry", label = label, rev = rev)
252 yield self.t("naventry", label = label, rev = rev)
255
253
256 yield self.t("naventry", label="tip")
254 yield self.t("naventry", label="tip")
257
255
258 def changelist():
256 def changelist():
259 parity = (start - end) & 1
257 parity = (start - end) & 1
260 cl = self.repo.changelog
258 cl = self.repo.changelog
261 l = [] # build a list in forward order for efficiency
259 l = [] # build a list in forward order for efficiency
262 for i in range(start, end):
260 for i in range(start, end):
263 n = cl.node(i)
261 n = cl.node(i)
264 changes = cl.read(n)
262 changes = cl.read(n)
265 hn = hex(n)
263 hn = hex(n)
266 t = float(changes[2].split(' ')[0])
264 t = float(changes[2].split(' ')[0])
267
265
268 l.insert(0, self.t(
266 l.insert(0, self.t(
269 'changelogentry',
267 'changelogentry',
270 parity = parity,
268 parity = parity,
271 author = changes[1],
269 author = changes[1],
272 parent = self.parents("changelogparent",
270 parent = self.parents("changelogparent",
273 cl.parents(n), cl.rev),
271 cl.parents(n), cl.rev),
274 changelogtag = self.showtag("changelogtag",n),
272 changelogtag = self.showtag("changelogtag",n),
275 manifest = hex(changes[0]),
273 manifest = hex(changes[0]),
276 desc = changes[4],
274 desc = changes[4],
277 date = t,
275 date = t,
278 files = self.listfilediffs(changes[3], n),
276 files = self.listfilediffs(changes[3], n),
279 rev = i,
277 rev = i,
280 node = hn))
278 node = hn))
281 parity = 1 - parity
279 parity = 1 - parity
282
280
283 yield l
281 yield l
284
282
285 cl = self.repo.changelog
283 cl = self.repo.changelog
286 mf = cl.read(cl.tip())[0]
284 mf = cl.read(cl.tip())[0]
287 count = cl.count()
285 count = cl.count()
288 start = max(0, pos - self.maxchanges + 1)
286 start = max(0, pos - self.maxchanges + 1)
289 end = min(count, start + self.maxchanges)
287 end = min(count, start + self.maxchanges)
290 pos = end - 1
288 pos = end - 1
291
289
292 yield self.t('changelog',
290 yield self.t('changelog',
293 header = self.header(),
294 footer = self.footer(),
295 repo = self.reponame,
296 changenav = changenav,
291 changenav = changenav,
297 manifest = hex(mf),
292 manifest = hex(mf),
298 rev = pos, changesets = count, entries = changelist)
293 rev = pos, changesets = count, entries = changelist)
299
294
300 def search(self, query):
295 def search(self, query):
301
296
302 def changelist():
297 def changelist():
303 cl = self.repo.changelog
298 cl = self.repo.changelog
304 count = 0
299 count = 0
305 qw = query.lower().split()
300 qw = query.lower().split()
306
301
307 def revgen():
302 def revgen():
308 for i in range(cl.count() - 1, 0, -100):
303 for i in range(cl.count() - 1, 0, -100):
309 l = []
304 l = []
310 for j in range(max(0, i - 100), i):
305 for j in range(max(0, i - 100), i):
311 n = cl.node(j)
306 n = cl.node(j)
312 changes = cl.read(n)
307 changes = cl.read(n)
313 l.insert(0, (n, j, changes))
308 l.insert(0, (n, j, changes))
314 for e in l:
309 for e in l:
315 yield e
310 yield e
316
311
317 for n, i, changes in revgen():
312 for n, i, changes in revgen():
318 miss = 0
313 miss = 0
319 for q in qw:
314 for q in qw:
320 if not (q in changes[1].lower() or
315 if not (q in changes[1].lower() or
321 q in changes[4].lower() or
316 q in changes[4].lower() or
322 q in " ".join(changes[3][:20]).lower()):
317 q in " ".join(changes[3][:20]).lower()):
323 miss = 1
318 miss = 1
324 break
319 break
325 if miss: continue
320 if miss: continue
326
321
327 count += 1
322 count += 1
328 hn = hex(n)
323 hn = hex(n)
329 t = float(changes[2].split(' ')[0])
324 t = float(changes[2].split(' ')[0])
330
325
331 yield self.t(
326 yield self.t(
332 'searchentry',
327 'searchentry',
333 parity = count & 1,
328 parity = count & 1,
334 author = changes[1],
329 author = changes[1],
335 parent = self.parents("changelogparent",
330 parent = self.parents("changelogparent",
336 cl.parents(n), cl.rev),
331 cl.parents(n), cl.rev),
337 changelogtag = self.showtag("changelogtag",n),
332 changelogtag = self.showtag("changelogtag",n),
338 manifest = hex(changes[0]),
333 manifest = hex(changes[0]),
339 desc = changes[4],
334 desc = changes[4],
340 date = t,
335 date = t,
341 files = self.listfilediffs(changes[3], n),
336 files = self.listfilediffs(changes[3], n),
342 rev = i,
337 rev = i,
343 node = hn)
338 node = hn)
344
339
345 if count >= self.maxchanges: break
340 if count >= self.maxchanges: break
346
341
347 cl = self.repo.changelog
342 cl = self.repo.changelog
348 mf = cl.read(cl.tip())[0]
343 mf = cl.read(cl.tip())[0]
349
344
350 yield self.t('search',
345 yield self.t('search',
351 header = self.header(),
352 footer = self.footer(),
353 query = query,
346 query = query,
354 repo = self.reponame,
355 manifest = hex(mf),
347 manifest = hex(mf),
356 entries = changelist)
348 entries = changelist)
357
349
358 def changeset(self, nodeid):
350 def changeset(self, nodeid):
359 n = bin(nodeid)
351 n = bin(nodeid)
360 cl = self.repo.changelog
352 cl = self.repo.changelog
361 changes = cl.read(n)
353 changes = cl.read(n)
362 p1 = cl.parents(n)[0]
354 p1 = cl.parents(n)[0]
363 t = float(changes[2].split(' ')[0])
355 t = float(changes[2].split(' ')[0])
364
356
365 files = []
357 files = []
366 mf = self.repo.manifest.read(changes[0])
358 mf = self.repo.manifest.read(changes[0])
367 for f in changes[3]:
359 for f in changes[3]:
368 files.append(self.t("filenodelink",
360 files.append(self.t("filenodelink",
369 filenode = hex(mf.get(f, nullid)), file = f))
361 filenode = hex(mf.get(f, nullid)), file = f))
370
362
371 def diff():
363 def diff():
372 yield self.diff(p1, n, changes[3])
364 yield self.diff(p1, n, changes[3])
373
365
374 yield self.t('changeset',
366 yield self.t('changeset',
375 header = self.header(),
376 footer = self.footer(),
377 repo = self.reponame,
378 diff = diff,
367 diff = diff,
379 rev = cl.rev(n),
368 rev = cl.rev(n),
380 node = nodeid,
369 node = nodeid,
381 parent = self.parents("changesetparent",
370 parent = self.parents("changesetparent",
382 cl.parents(n), cl.rev),
371 cl.parents(n), cl.rev),
383 changesettag = self.showtag("changesettag",n),
372 changesettag = self.showtag("changesettag",n),
384 manifest = hex(changes[0]),
373 manifest = hex(changes[0]),
385 author = changes[1],
374 author = changes[1],
386 desc = changes[4],
375 desc = changes[4],
387 date = t,
376 date = t,
388 files = files)
377 files = files)
389
378
390 def filelog(self, f, filenode):
379 def filelog(self, f, filenode):
391 cl = self.repo.changelog
380 cl = self.repo.changelog
392 fl = self.repo.file(f)
381 fl = self.repo.file(f)
393 count = fl.count()
382 count = fl.count()
394
383
395 def entries():
384 def entries():
396 l = []
385 l = []
397 parity = (count - 1) & 1
386 parity = (count - 1) & 1
398
387
399 for i in range(count):
388 for i in range(count):
400
389
401 n = fl.node(i)
390 n = fl.node(i)
402 lr = fl.linkrev(n)
391 lr = fl.linkrev(n)
403 cn = cl.node(lr)
392 cn = cl.node(lr)
404 cs = cl.read(cl.node(lr))
393 cs = cl.read(cl.node(lr))
405 t = float(cs[2].split(' ')[0])
394 t = float(cs[2].split(' ')[0])
406
395
407 l.insert(0, self.t("filelogentry",
396 l.insert(0, self.t("filelogentry",
408 parity = parity,
397 parity = parity,
409 filenode = hex(n),
398 filenode = hex(n),
410 filerev = i,
399 filerev = i,
411 file = f,
400 file = f,
412 node = hex(cn),
401 node = hex(cn),
413 author = cs[1],
402 author = cs[1],
414 date = t,
403 date = t,
415 parent = self.parents("filelogparent",
404 parent = self.parents("filelogparent",
416 fl.parents(n), fl.rev, file=f),
405 fl.parents(n), fl.rev, file=f),
417 desc = cs[4]))
406 desc = cs[4]))
418 parity = 1 - parity
407 parity = 1 - parity
419
408
420 yield l
409 yield l
421
410
422 yield self.t("filelog",
411 yield self.t("filelog",
423 header = self.header(),
424 footer = self.footer(),
425 repo = self.reponame,
426 file = f,
412 file = f,
427 filenode = filenode,
413 filenode = filenode,
428 entries = entries)
414 entries = entries)
429
415
430 def filerevision(self, f, node):
416 def filerevision(self, f, node):
431 fl = self.repo.file(f)
417 fl = self.repo.file(f)
432 n = bin(node)
418 n = bin(node)
433 text = fl.read(n)
419 text = fl.read(n)
434 changerev = fl.linkrev(n)
420 changerev = fl.linkrev(n)
435 cl = self.repo.changelog
421 cl = self.repo.changelog
436 cn = cl.node(changerev)
422 cn = cl.node(changerev)
437 cs = cl.read(cn)
423 cs = cl.read(cn)
438 t = float(cs[2].split(' ')[0])
424 t = float(cs[2].split(' ')[0])
439 mfn = cs[0]
425 mfn = cs[0]
440
426
441 def lines():
427 def lines():
442 for l, t in enumerate(text.splitlines(1)):
428 for l, t in enumerate(text.splitlines(1)):
443 yield self.t("fileline", line = t,
429 yield self.t("fileline", line = t,
444 linenumber = "% 6d" % (l + 1),
430 linenumber = "% 6d" % (l + 1),
445 parity = l & 1)
431 parity = l & 1)
446
432
447 yield self.t("filerevision", file = f,
433 yield self.t("filerevision", file = f,
448 header = self.header(),
449 footer = self.footer(),
450 repo = self.reponame,
451 filenode = node,
434 filenode = node,
452 path = up(f),
435 path = up(f),
453 text = lines(),
436 text = lines(),
454 rev = changerev,
437 rev = changerev,
455 node = hex(cn),
438 node = hex(cn),
456 manifest = hex(mfn),
439 manifest = hex(mfn),
457 author = cs[1],
440 author = cs[1],
458 date = t,
441 date = t,
459 parent = self.parents("filerevparent",
442 parent = self.parents("filerevparent",
460 fl.parents(n), fl.rev, file=f),
443 fl.parents(n), fl.rev, file=f),
461 permissions = self.repo.manifest.readflags(mfn)[f])
444 permissions = self.repo.manifest.readflags(mfn)[f])
462
445
463 def fileannotate(self, f, node):
446 def fileannotate(self, f, node):
464 bcache = {}
447 bcache = {}
465 ncache = {}
448 ncache = {}
466 fl = self.repo.file(f)
449 fl = self.repo.file(f)
467 n = bin(node)
450 n = bin(node)
468 changerev = fl.linkrev(n)
451 changerev = fl.linkrev(n)
469
452
470 cl = self.repo.changelog
453 cl = self.repo.changelog
471 cn = cl.node(changerev)
454 cn = cl.node(changerev)
472 cs = cl.read(cn)
455 cs = cl.read(cn)
473 t = float(cs[2].split(' ')[0])
456 t = float(cs[2].split(' ')[0])
474 mfn = cs[0]
457 mfn = cs[0]
475
458
476 def annotate():
459 def annotate():
477 parity = 1
460 parity = 1
478 last = None
461 last = None
479 for r, l in fl.annotate(n):
462 for r, l in fl.annotate(n):
480 try:
463 try:
481 cnode = ncache[r]
464 cnode = ncache[r]
482 except KeyError:
465 except KeyError:
483 cnode = ncache[r] = self.repo.changelog.node(r)
466 cnode = ncache[r] = self.repo.changelog.node(r)
484
467
485 try:
468 try:
486 name = bcache[r]
469 name = bcache[r]
487 except KeyError:
470 except KeyError:
488 cl = self.repo.changelog.read(cnode)
471 cl = self.repo.changelog.read(cnode)
489 name = cl[1]
472 name = cl[1]
490 f = name.find('@')
473 f = name.find('@')
491 if f >= 0:
474 if f >= 0:
492 name = name[:f]
475 name = name[:f]
493 f = name.find('<')
476 f = name.find('<')
494 if f >= 0:
477 if f >= 0:
495 name = name[f+1:]
478 name = name[f+1:]
496 bcache[r] = name
479 bcache[r] = name
497
480
498 if last != cnode:
481 if last != cnode:
499 parity = 1 - parity
482 parity = 1 - parity
500 last = cnode
483 last = cnode
501
484
502 yield self.t("annotateline",
485 yield self.t("annotateline",
503 parity = parity,
486 parity = parity,
504 node = hex(cnode),
487 node = hex(cnode),
505 rev = r,
488 rev = r,
506 author = name,
489 author = name,
507 file = f,
490 file = f,
508 line = l)
491 line = l)
509
492
510 yield self.t("fileannotate",
493 yield self.t("fileannotate",
511 header = self.header(),
512 footer = self.footer(),
513 repo = self.reponame,
514 file = f,
494 file = f,
515 filenode = node,
495 filenode = node,
516 annotate = annotate,
496 annotate = annotate,
517 path = up(f),
497 path = up(f),
518 rev = changerev,
498 rev = changerev,
519 node = hex(cn),
499 node = hex(cn),
520 manifest = hex(mfn),
500 manifest = hex(mfn),
521 author = cs[1],
501 author = cs[1],
522 date = t,
502 date = t,
523 parent = self.parents("fileannotateparent",
503 parent = self.parents("fileannotateparent",
524 fl.parents(n), fl.rev, file=f),
504 fl.parents(n), fl.rev, file=f),
525 permissions = self.repo.manifest.readflags(mfn)[f])
505 permissions = self.repo.manifest.readflags(mfn)[f])
526
506
527 def manifest(self, mnode, path):
507 def manifest(self, mnode, path):
528 mf = self.repo.manifest.read(bin(mnode))
508 mf = self.repo.manifest.read(bin(mnode))
529 rev = self.repo.manifest.rev(bin(mnode))
509 rev = self.repo.manifest.rev(bin(mnode))
530 node = self.repo.changelog.node(rev)
510 node = self.repo.changelog.node(rev)
531 mff=self.repo.manifest.readflags(bin(mnode))
511 mff=self.repo.manifest.readflags(bin(mnode))
532
512
533 files = {}
513 files = {}
534
514
535 p = path[1:]
515 p = path[1:]
536 l = len(p)
516 l = len(p)
537
517
538 for f,n in mf.items():
518 for f,n in mf.items():
539 if f[:l] != p:
519 if f[:l] != p:
540 continue
520 continue
541 remain = f[l:]
521 remain = f[l:]
542 if "/" in remain:
522 if "/" in remain:
543 short = remain[:remain.find("/") + 1] # bleah
523 short = remain[:remain.find("/") + 1] # bleah
544 files[short] = (f, None)
524 files[short] = (f, None)
545 else:
525 else:
546 short = os.path.basename(remain)
526 short = os.path.basename(remain)
547 files[short] = (f, n)
527 files[short] = (f, n)
548
528
549 def filelist():
529 def filelist():
550 parity = 0
530 parity = 0
551 fl = files.keys()
531 fl = files.keys()
552 fl.sort()
532 fl.sort()
553 for f in fl:
533 for f in fl:
554 full, fnode = files[f]
534 full, fnode = files[f]
555 if fnode:
535 if fnode:
556 yield self.t("manifestfileentry",
536 yield self.t("manifestfileentry",
557 file = full,
537 file = full,
558 manifest = mnode,
538 manifest = mnode,
559 filenode = hex(fnode),
539 filenode = hex(fnode),
560 parity = parity,
540 parity = parity,
561 basename = f,
541 basename = f,
562 permissions = mff[full])
542 permissions = mff[full])
563 else:
543 else:
564 yield self.t("manifestdirentry",
544 yield self.t("manifestdirentry",
565 parity = parity,
545 parity = parity,
566 path = os.path.join(path, f),
546 path = os.path.join(path, f),
567 manifest = mnode, basename = f[:-1])
547 manifest = mnode, basename = f[:-1])
568 parity = 1 - parity
548 parity = 1 - parity
569
549
570 yield self.t("manifest",
550 yield self.t("manifest",
571 header = self.header(),
572 footer = self.footer(),
573 repo = self.reponame,
574 manifest = mnode,
551 manifest = mnode,
575 rev = rev,
552 rev = rev,
576 node = hex(node),
553 node = hex(node),
577 path = path,
554 path = path,
578 up = up(path),
555 up = up(path),
579 entries = filelist)
556 entries = filelist)
580
557
581 def tags(self):
558 def tags(self):
582 cl = self.repo.changelog
559 cl = self.repo.changelog
583 mf = cl.read(cl.tip())[0]
560 mf = cl.read(cl.tip())[0]
584
561
585 i = self.repo.tagslist()
562 i = self.repo.tagslist()
586 i.reverse()
563 i.reverse()
587
564
588 def entries():
565 def entries():
589 parity = 0
566 parity = 0
590 for k,n in i:
567 for k,n in i:
591 yield self.t("tagentry",
568 yield self.t("tagentry",
592 parity = parity,
569 parity = parity,
593 tag = k,
570 tag = k,
594 node = hex(n))
571 node = hex(n))
595 parity = 1 - parity
572 parity = 1 - parity
596
573
597 yield self.t("tags",
574 yield self.t("tags",
598 header = self.header(),
599 footer = self.footer(),
600 repo = self.reponame,
601 manifest = hex(mf),
575 manifest = hex(mf),
602 entries = entries)
576 entries = entries)
603
577
604 def filediff(self, file, changeset):
578 def filediff(self, file, changeset):
605 n = bin(changeset)
579 n = bin(changeset)
606 cl = self.repo.changelog
580 cl = self.repo.changelog
607 p1 = cl.parents(n)[0]
581 p1 = cl.parents(n)[0]
608 cs = cl.read(n)
582 cs = cl.read(n)
609 mf = self.repo.manifest.read(cs[0])
583 mf = self.repo.manifest.read(cs[0])
610
584
611 def diff():
585 def diff():
612 yield self.diff(p1, n, file)
586 yield self.diff(p1, n, file)
613
587
614 yield self.t("filediff",
588 yield self.t("filediff",
615 header = self.header(),
616 footer = self.footer(),
617 repo = self.reponame,
618 file = file,
589 file = file,
619 filenode = hex(mf.get(file, nullid)),
590 filenode = hex(mf.get(file, nullid)),
620 node = changeset,
591 node = changeset,
621 rev = self.repo.changelog.rev(n),
592 rev = self.repo.changelog.rev(n),
622 parent = self.parents("filediffparent",
593 parent = self.parents("filediffparent",
623 cl.parents(n), cl.rev),
594 cl.parents(n), cl.rev),
624 diff = diff)
595 diff = diff)
625
596
626 # add tags to things
597 # add tags to things
627 # tags -> list of changesets corresponding to tags
598 # tags -> list of changesets corresponding to tags
628 # find tag, changeset, file
599 # find tag, changeset, file
629
600
630 def run(self):
601 def run(self):
631 self.refresh()
602 self.refresh()
632 args = cgi.parse()
603 args = cgi.parse()
633
604
634 m = os.path.join(self.templates, "map")
605 m = os.path.join(self.templates, "map")
635 if args.has_key('style'):
606 if args.has_key('style'):
636 b = os.path.basename("map-" + args['style'][0])
607 b = os.path.basename("map-" + args['style'][0])
637 p = os.path.join(self.templates, b)
608 p = os.path.join(self.templates, b)
638 if os.path.isfile(p): m = p
609 if os.path.isfile(p): m = p
639
610
640 self.t = templater(m, self.filters)
611 port = os.environ["SERVER_PORT"]
612 port = port != "80" and (":" + port) or ""
613 url = "http://%s%s%s" % \
614 (os.environ["SERVER_NAME"], port, os.environ["REQUEST_URI"])
615
616 self.t = templater(m, self.filters,
617 {"url":url,
618 "repo":self.reponame,
619 "header":self.header(),
620 "footer":self.footer(),
621 })
641
622
642 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
623 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
643 c = self.repo.changelog.count() - 1
624 c = self.repo.changelog.count() - 1
644 hi = c
625 hi = c
645 if args.has_key('rev'):
626 if args.has_key('rev'):
646 hi = args['rev'][0]
627 hi = args['rev'][0]
647 try:
628 try:
648 hi = self.repo.changelog.rev(self.repo.lookup(hi))
629 hi = self.repo.changelog.rev(self.repo.lookup(hi))
649 except KeyError:
630 except KeyError:
650 write(self.search(hi))
631 write(self.search(hi))
651 return
632 return
652
633
653 write(self.changelog(hi))
634 write(self.changelog(hi))
654
635
655 elif args['cmd'][0] == 'changeset':
636 elif args['cmd'][0] == 'changeset':
656 write(self.changeset(args['node'][0]))
637 write(self.changeset(args['node'][0]))
657
638
658 elif args['cmd'][0] == 'manifest':
639 elif args['cmd'][0] == 'manifest':
659 write(self.manifest(args['manifest'][0], args['path'][0]))
640 write(self.manifest(args['manifest'][0], args['path'][0]))
660
641
661 elif args['cmd'][0] == 'tags':
642 elif args['cmd'][0] == 'tags':
662 write(self.tags())
643 write(self.tags())
663
644
664 elif args['cmd'][0] == 'filediff':
645 elif args['cmd'][0] == 'filediff':
665 write(self.filediff(args['file'][0], args['node'][0]))
646 write(self.filediff(args['file'][0], args['node'][0]))
666
647
667 elif args['cmd'][0] == 'file':
648 elif args['cmd'][0] == 'file':
668 write(self.filerevision(args['file'][0], args['filenode'][0]))
649 write(self.filerevision(args['file'][0], args['filenode'][0]))
669
650
670 elif args['cmd'][0] == 'annotate':
651 elif args['cmd'][0] == 'annotate':
671 write(self.fileannotate(args['file'][0], args['filenode'][0]))
652 write(self.fileannotate(args['file'][0], args['filenode'][0]))
672
653
673 elif args['cmd'][0] == 'filelog':
654 elif args['cmd'][0] == 'filelog':
674 write(self.filelog(args['file'][0], args['filenode'][0]))
655 write(self.filelog(args['file'][0], args['filenode'][0]))
675
656
676 elif args['cmd'][0] == 'heads':
657 elif args['cmd'][0] == 'heads':
677 httphdr("text/plain")
658 httphdr("text/plain")
678 h = self.repo.heads()
659 h = self.repo.heads()
679 sys.stdout.write(" ".join(map(hex, h)) + "\n")
660 sys.stdout.write(" ".join(map(hex, h)) + "\n")
680
661
681 elif args['cmd'][0] == 'branches':
662 elif args['cmd'][0] == 'branches':
682 httphdr("text/plain")
663 httphdr("text/plain")
683 nodes = []
664 nodes = []
684 if args.has_key('nodes'):
665 if args.has_key('nodes'):
685 nodes = map(bin, args['nodes'][0].split(" "))
666 nodes = map(bin, args['nodes'][0].split(" "))
686 for b in self.repo.branches(nodes):
667 for b in self.repo.branches(nodes):
687 sys.stdout.write(" ".join(map(hex, b)) + "\n")
668 sys.stdout.write(" ".join(map(hex, b)) + "\n")
688
669
689 elif args['cmd'][0] == 'between':
670 elif args['cmd'][0] == 'between':
690 httphdr("text/plain")
671 httphdr("text/plain")
691 nodes = []
672 nodes = []
692 if args.has_key('pairs'):
673 if args.has_key('pairs'):
693 pairs = [ map(bin, p.split("-"))
674 pairs = [ map(bin, p.split("-"))
694 for p in args['pairs'][0].split(" ") ]
675 for p in args['pairs'][0].split(" ") ]
695 for b in self.repo.between(pairs):
676 for b in self.repo.between(pairs):
696 sys.stdout.write(" ".join(map(hex, b)) + "\n")
677 sys.stdout.write(" ".join(map(hex, b)) + "\n")
697
678
698 elif args['cmd'][0] == 'changegroup':
679 elif args['cmd'][0] == 'changegroup':
699 httphdr("application/hg-changegroup")
680 httphdr("application/hg-changegroup")
700 nodes = []
681 nodes = []
701 if self.viewonly:
682 if self.viewonly:
702 return
683 return
703
684
704 if args.has_key('roots'):
685 if args.has_key('roots'):
705 nodes = map(bin, args['roots'][0].split(" "))
686 nodes = map(bin, args['roots'][0].split(" "))
706
687
707 z = zlib.compressobj()
688 z = zlib.compressobj()
708 for chunk in self.repo.changegroup(nodes):
689 for chunk in self.repo.changegroup(nodes):
709 sys.stdout.write(z.compress(chunk))
690 sys.stdout.write(z.compress(chunk))
710
691
711 sys.stdout.write(z.flush())
692 sys.stdout.write(z.flush())
712
693
713 else:
694 else:
714 write(self.t("error"))
695 write(self.t("error"))
715
696
716 def server(path, name, templates, address, port):
697 def server(path, name, templates, address, port):
717
698
718 import BaseHTTPServer
699 import BaseHTTPServer
719 import sys, os
700 import sys, os
720
701
721 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
702 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
722 def do_POST(self):
703 def do_POST(self):
723 try:
704 try:
724 self.do_hgweb()
705 self.do_hgweb()
725 except socket.error, inst:
706 except socket.error, inst:
726 if inst.args[0] != 32: raise
707 if inst.args[0] != 32: raise
727
708
728 def do_GET(self):
709 def do_GET(self):
729 self.do_POST()
710 self.do_POST()
730
711
731 def do_hgweb(self):
712 def do_hgweb(self):
732 query = ""
713 query = ""
733 p = self.path.find("?")
714 p = self.path.find("?")
734 if p:
715 if p:
735 query = self.path[p + 1:]
716 query = self.path[p + 1:]
736 query = query.replace('+', ' ')
717 query = query.replace('+', ' ')
737
718
738 env = {}
719 env = {}
739 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
720 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
740 env['REQUEST_METHOD'] = self.command
721 env['REQUEST_METHOD'] = self.command
741 env['SERVER_NAME'] = self.server.server_name
722 env['SERVER_NAME'] = self.server.server_name
742 env['SERVER_PORT'] = str(self.server.server_port)
723 env['SERVER_PORT'] = str(self.server.server_port)
743 env['REQUEST_URI'] = "/"
724 env['REQUEST_URI'] = "/"
744 if query:
725 if query:
745 env['QUERY_STRING'] = query
726 env['QUERY_STRING'] = query
746 host = self.address_string()
727 host = self.address_string()
747 if host != self.client_address[0]:
728 if host != self.client_address[0]:
748 env['REMOTE_HOST'] = host
729 env['REMOTE_HOST'] = host
749 env['REMOTE_ADDR'] = self.client_address[0]
730 env['REMOTE_ADDR'] = self.client_address[0]
750
731
751 if self.headers.typeheader is None:
732 if self.headers.typeheader is None:
752 env['CONTENT_TYPE'] = self.headers.type
733 env['CONTENT_TYPE'] = self.headers.type
753 else:
734 else:
754 env['CONTENT_TYPE'] = self.headers.typeheader
735 env['CONTENT_TYPE'] = self.headers.typeheader
755 length = self.headers.getheader('content-length')
736 length = self.headers.getheader('content-length')
756 if length:
737 if length:
757 env['CONTENT_LENGTH'] = length
738 env['CONTENT_LENGTH'] = length
758 accept = []
739 accept = []
759 for line in self.headers.getallmatchingheaders('accept'):
740 for line in self.headers.getallmatchingheaders('accept'):
760 if line[:1] in "\t\n\r ":
741 if line[:1] in "\t\n\r ":
761 accept.append(line.strip())
742 accept.append(line.strip())
762 else:
743 else:
763 accept = accept + line[7:].split(',')
744 accept = accept + line[7:].split(',')
764 env['HTTP_ACCEPT'] = ','.join(accept)
745 env['HTTP_ACCEPT'] = ','.join(accept)
765
746
766 os.environ.update(env)
747 os.environ.update(env)
767
748
768 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
749 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
769 try:
750 try:
770 sys.stdin = self.rfile
751 sys.stdin = self.rfile
771 sys.stdout = self.wfile
752 sys.stdout = self.wfile
772 sys.argv = ["hgweb.py"]
753 sys.argv = ["hgweb.py"]
773 if '=' not in query:
754 if '=' not in query:
774 sys.argv.append(query)
755 sys.argv.append(query)
775 self.send_response(200, "Script output follows")
756 self.send_response(200, "Script output follows")
776 hg.run()
757 hg.run()
777 finally:
758 finally:
778 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
759 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
779
760
780 hg = hgweb(path, name, templates)
761 hg = hgweb(path, name, templates)
781 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
762 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
782 httpd.serve_forever()
763 httpd.serve_forever()
@@ -1,7 +1,7 b''
1 <item>
1 <item>
2 <title>#desc|firstline|escape#</title>
2 <title>#desc|firstline|escape#</title>
3 <link>http://127.0.0.1:8000/?cmd=changeset;node=#node#"</link>
3 <link>#url#?cmd=changeset;node=#node#"</link>
4 <description>#desc|escape|addbreaks#</description>
4 <description>#desc|escape|addbreaks#</description>
5 <author>#author|obfuscate#</author>
5 <author>#author|obfuscate#</author>
6 <pubDate>#date|rfc822date#</pubDate>
6 <pubDate>#date|rfc822date#</pubDate>
7 </item>
7 </item>
@@ -1,7 +1,7 b''
1 <item>
1 <item>
2 <title>#desc|firstline|escape#</title>
2 <title>#desc|firstline|escape#</title>
3 <link>http://127.0.0.1:8000/?cmd=file;file=#file#;filenode=#filenode#"</link>
3 <link>#url#?cmd=file;file=#file#;filenode=#filenode#"</link>
4 <description>#desc|escape|addbreaks#</description>
4 <description>#desc|escape|addbreaks#</description>
5 <author>#author|obfuscate#</author>
5 <author>#author|obfuscate#</author>
6 <pubDate>#date|rfc822date#</pubDate>>
6 <pubDate>#date|rfc822date#</pubDate>>
7 </item>
7 </item>
General Comments 0
You need to be logged in to leave comments. Login now