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