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