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