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