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