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