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