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