##// END OF EJS Templates
- duplicate the parent link logic to show child links...
Muli Ben-Yehuda -
r1606:ba625c80 default
parent child Browse files
Show More
@@ -1,1100 +1,1109 b''
1 1 # hgweb.py - web interface to a mercurial repository
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
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 import os, cgi, sys
10 10 from demandload import demandload
11 11 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
12 12 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
13 13 demandload(globals(), "mimetypes")
14 14 from node import *
15 15 from i18n import gettext as _
16 16
17 17 def templatepath():
18 18 for f in "templates", "../templates":
19 19 p = os.path.join(os.path.dirname(__file__), f)
20 20 if os.path.isdir(p):
21 21 return p
22 22
23 23 def age(x):
24 24 def plural(t, c):
25 25 if c == 1:
26 26 return t
27 27 return t + "s"
28 28 def fmt(t, c):
29 29 return "%d %s" % (c, plural(t, c))
30 30
31 31 now = time.time()
32 32 then = x[0]
33 33 delta = max(1, int(now - then))
34 34
35 35 scales = [["second", 1],
36 36 ["minute", 60],
37 37 ["hour", 3600],
38 38 ["day", 3600 * 24],
39 39 ["week", 3600 * 24 * 7],
40 40 ["month", 3600 * 24 * 30],
41 41 ["year", 3600 * 24 * 365]]
42 42
43 43 scales.reverse()
44 44
45 45 for t, s in scales:
46 46 n = delta / s
47 47 if n >= 2 or s == 1:
48 48 return fmt(t, n)
49 49
50 50 def nl2br(text):
51 51 return text.replace('\n', '<br/>\n')
52 52
53 53 def obfuscate(text):
54 54 return ''.join(['&#%d;' % ord(c) for c in text])
55 55
56 56 def up(p):
57 57 if p[0] != "/":
58 58 p = "/" + p
59 59 if p[-1] == "/":
60 60 p = p[:-1]
61 61 up = os.path.dirname(p)
62 62 if up == "/":
63 63 return "/"
64 64 return up + "/"
65 65
66 66 def get_mtime(repo_path):
67 67 hg_path = os.path.join(repo_path, ".hg")
68 68 cl_path = os.path.join(hg_path, "00changelog.i")
69 69 if os.path.exists(os.path.join(cl_path)):
70 70 return os.stat(cl_path).st_mtime
71 71 else:
72 72 return os.stat(hg_path).st_mtime
73 73
74 74 class hgrequest(object):
75 75 def __init__(self, inp=None, out=None, env=None):
76 76 self.inp = inp or sys.stdin
77 77 self.out = out or sys.stdout
78 78 self.env = env or os.environ
79 79 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
80 80
81 81 def write(self, *things):
82 82 for thing in things:
83 83 if hasattr(thing, "__iter__"):
84 84 for part in thing:
85 85 self.write(part)
86 86 else:
87 87 try:
88 88 self.out.write(str(thing))
89 89 except socket.error, inst:
90 90 if inst[0] != errno.ECONNRESET:
91 91 raise
92 92
93 93 def header(self, headers=[('Content-type','text/html')]):
94 94 for header in headers:
95 95 self.out.write("%s: %s\r\n" % header)
96 96 self.out.write("\r\n")
97 97
98 98 def httphdr(self, type, file="", size=0):
99 99
100 100 headers = [('Content-type', type)]
101 101 if file:
102 102 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
103 103 if size > 0:
104 104 headers.append(('Content-length', str(size)))
105 105 self.header(headers)
106 106
107 107 class templater(object):
108 108 def __init__(self, mapfile, filters={}, defaults={}):
109 109 self.cache = {}
110 110 self.map = {}
111 111 self.base = os.path.dirname(mapfile)
112 112 self.filters = filters
113 113 self.defaults = defaults
114 114
115 115 for l in file(mapfile):
116 116 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
117 117 if m:
118 118 self.cache[m.group(1)] = m.group(2)
119 119 else:
120 120 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
121 121 if m:
122 122 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
123 123 else:
124 124 raise LookupError(_("unknown map entry '%s'") % l)
125 125
126 126 def __call__(self, t, **map):
127 127 m = self.defaults.copy()
128 128 m.update(map)
129 129 try:
130 130 tmpl = self.cache[t]
131 131 except KeyError:
132 132 tmpl = self.cache[t] = file(self.map[t]).read()
133 133 return self.template(tmpl, self.filters, **m)
134 134
135 135 def template(self, tmpl, filters={}, **map):
136 136 while tmpl:
137 137 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
138 138 if m:
139 139 yield tmpl[:m.start(0)]
140 140 v = map.get(m.group(1), "")
141 141 v = callable(v) and v(**map) or v
142 142
143 143 format = m.group(2)
144 144 fl = m.group(4)
145 145
146 146 if format:
147 147 q = v.__iter__
148 148 for i in q():
149 149 lm = map.copy()
150 150 lm.update(i)
151 151 yield self(format[1:], **lm)
152 152
153 153 v = ""
154 154
155 155 elif fl:
156 156 for f in fl.split("|")[1:]:
157 157 v = filters[f](v)
158 158
159 159 yield v
160 160 tmpl = tmpl[m.end(0):]
161 161 else:
162 162 yield tmpl
163 163 return
164 164
165 165 common_filters = {
166 166 "escape": cgi.escape,
167 167 "strip": lambda x: x.strip(),
168 168 "age": age,
169 169 "date": lambda x: util.datestr(x),
170 170 "addbreaks": nl2br,
171 171 "obfuscate": obfuscate,
172 172 "short": (lambda x: x[:12]),
173 173 "firstline": (lambda x: x.splitlines(1)[0]),
174 174 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
175 175 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
176 176 }
177 177
178 178 class hgweb(object):
179 179 def __init__(self, repo, name=None):
180 180 if type(repo) == type(""):
181 181 self.repo = hg.repository(ui.ui(), repo)
182 182 else:
183 183 self.repo = repo
184 184
185 185 self.mtime = -1
186 186 self.reponame = name
187 187 self.archives = 'zip', 'gz', 'bz2'
188 188
189 189 def refresh(self):
190 190 mtime = get_mtime(self.repo.root)
191 191 if mtime != self.mtime:
192 192 self.mtime = mtime
193 193 self.repo = hg.repository(self.repo.ui, self.repo.root)
194 194 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
195 195 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
196 196 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
197 197
198 198 def archivelist(self, nodeid):
199 199 for i in self.archives:
200 200 if self.repo.ui.configbool("web", "allow" + i, False):
201 201 yield {"type" : i, "node" : nodeid}
202 202
203 203 def listfiles(self, files, mf):
204 204 for f in files[:self.maxfiles]:
205 205 yield self.t("filenodelink", node=hex(mf[f]), file=f)
206 206 if len(files) > self.maxfiles:
207 207 yield self.t("fileellipses")
208 208
209 209 def listfilediffs(self, files, changeset):
210 210 for f in files[:self.maxfiles]:
211 211 yield self.t("filedifflink", node=hex(changeset), file=f)
212 212 if len(files) > self.maxfiles:
213 213 yield self.t("fileellipses")
214 214
215 def parents(self, node, parents=[], rev=None, hide=False, **args):
215 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
216 216 if not rev:
217 217 rev = lambda x: ""
218 parents = [p for p in parents if p != nullid]
219 if hide and len(parents) == 1 and rev(parents[0]) == rev(node) - 1:
218 siblings = [s for s in siblings if s != nullid]
219 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
220 220 return
221 for p in parents:
222 yield dict(node=hex(p), rev=rev(p), **args)
221 for s in siblings:
222 yield dict(node=hex(s), rev=rev(s), **args)
223 223
224 224 def showtag(self, t1, node=nullid, **args):
225 225 for t in self.repo.nodetags(node):
226 226 yield self.t(t1, tag=t, **args)
227 227
228 228 def diff(self, node1, node2, files):
229 229 def filterfiles(list, files):
230 230 l = [x for x in list if x in files]
231 231
232 232 for f in files:
233 233 if f[-1] != os.sep:
234 234 f += os.sep
235 235 l += [x for x in list if x.startswith(f)]
236 236 return l
237 237
238 238 parity = [0]
239 239 def diffblock(diff, f, fn):
240 240 yield self.t("diffblock",
241 241 lines=prettyprintlines(diff),
242 242 parity=parity[0],
243 243 file=f,
244 244 filenode=hex(fn or nullid))
245 245 parity[0] = 1 - parity[0]
246 246
247 247 def prettyprintlines(diff):
248 248 for l in diff.splitlines(1):
249 249 if l.startswith('+'):
250 250 yield self.t("difflineplus", line=l)
251 251 elif l.startswith('-'):
252 252 yield self.t("difflineminus", line=l)
253 253 elif l.startswith('@'):
254 254 yield self.t("difflineat", line=l)
255 255 else:
256 256 yield self.t("diffline", line=l)
257 257
258 258 r = self.repo
259 259 cl = r.changelog
260 260 mf = r.manifest
261 261 change1 = cl.read(node1)
262 262 change2 = cl.read(node2)
263 263 mmap1 = mf.read(change1[0])
264 264 mmap2 = mf.read(change2[0])
265 265 date1 = util.datestr(change1[2])
266 266 date2 = util.datestr(change2[2])
267 267
268 268 c, a, d, u = r.changes(node1, node2)
269 269 if files:
270 270 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
271 271
272 272 for f in c:
273 273 to = r.file(f).read(mmap1[f])
274 274 tn = r.file(f).read(mmap2[f])
275 275 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
276 276 for f in a:
277 277 to = None
278 278 tn = r.file(f).read(mmap2[f])
279 279 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
280 280 for f in d:
281 281 to = r.file(f).read(mmap1[f])
282 282 tn = None
283 283 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
284 284
285 285 def changelog(self, pos):
286 286 def changenav(**map):
287 287 def seq(factor=1):
288 288 yield 1 * factor
289 289 yield 3 * factor
290 290 #yield 5 * factor
291 291 for f in seq(factor * 10):
292 292 yield f
293 293
294 294 l = []
295 295 for f in seq():
296 296 if f < self.maxchanges / 2:
297 297 continue
298 298 if f > count:
299 299 break
300 300 r = "%d" % f
301 301 if pos + f < count:
302 302 l.append(("+" + r, pos + f))
303 303 if pos - f >= 0:
304 304 l.insert(0, ("-" + r, pos - f))
305 305
306 306 yield {"rev": 0, "label": "(0)"}
307 307
308 308 for label, rev in l:
309 309 yield {"label": label, "rev": rev}
310 310
311 311 yield {"label": "tip", "rev": "tip"}
312 312
313 313 def changelist(**map):
314 314 parity = (start - end) & 1
315 315 cl = self.repo.changelog
316 316 l = [] # build a list in forward order for efficiency
317 317 for i in range(start, end):
318 318 n = cl.node(i)
319 319 changes = cl.read(n)
320 320 hn = hex(n)
321 321
322 322 l.insert(0, {"parity": parity,
323 323 "author": changes[1],
324 "parent": self.parents(n, cl.parents(n), cl.rev,
325 hide=True),
324 "parent": self.siblings(cl.parents(n), cl.rev,
325 cl.rev(n) - 1),
326 "child": self.siblings(cl.children(n), cl.rev,
327 cl.rev(n) + 1),
326 328 "changelogtag": self.showtag("changelogtag",n),
327 329 "manifest": hex(changes[0]),
328 330 "desc": changes[4],
329 331 "date": changes[2],
330 332 "files": self.listfilediffs(changes[3], n),
331 333 "rev": i,
332 334 "node": hn})
333 335 parity = 1 - parity
334 336
335 337 for e in l:
336 338 yield e
337 339
338 340 cl = self.repo.changelog
339 341 mf = cl.read(cl.tip())[0]
340 342 count = cl.count()
341 343 start = max(0, pos - self.maxchanges + 1)
342 344 end = min(count, start + self.maxchanges)
343 345 pos = end - 1
344 346
345 347 yield self.t('changelog',
346 348 changenav=changenav,
347 349 manifest=hex(mf),
348 350 rev=pos, changesets=count, entries=changelist)
349 351
350 352 def search(self, query):
351 353
352 354 def changelist(**map):
353 355 cl = self.repo.changelog
354 356 count = 0
355 357 qw = query.lower().split()
356 358
357 359 def revgen():
358 360 for i in range(cl.count() - 1, 0, -100):
359 361 l = []
360 362 for j in range(max(0, i - 100), i):
361 363 n = cl.node(j)
362 364 changes = cl.read(n)
363 365 l.append((n, j, changes))
364 366 l.reverse()
365 367 for e in l:
366 368 yield e
367 369
368 370 for n, i, changes in revgen():
369 371 miss = 0
370 372 for q in qw:
371 373 if not (q in changes[1].lower() or
372 374 q in changes[4].lower() or
373 375 q in " ".join(changes[3][:20]).lower()):
374 376 miss = 1
375 377 break
376 378 if miss:
377 379 continue
378 380
379 381 count += 1
380 382 hn = hex(n)
381 383
382 384 yield self.t('searchentry',
383 385 parity=count & 1,
384 386 author=changes[1],
385 parent=self.parents(n, cl.parents(n), cl.rev),
387 parent=self.siblings(cl.parents(n), cl.rev),
388 child=self.siblings(cl.children(n), cl.rev),
386 389 changelogtag=self.showtag("changelogtag",n),
387 390 manifest=hex(changes[0]),
388 391 desc=changes[4],
389 392 date=changes[2],
390 393 files=self.listfilediffs(changes[3], n),
391 394 rev=i,
392 395 node=hn)
393 396
394 397 if count >= self.maxchanges:
395 398 break
396 399
397 400 cl = self.repo.changelog
398 401 mf = cl.read(cl.tip())[0]
399 402
400 403 yield self.t('search',
401 404 query=query,
402 405 manifest=hex(mf),
403 406 entries=changelist)
404 407
405 408 def changeset(self, nodeid):
406 409 cl = self.repo.changelog
407 410 n = self.repo.lookup(nodeid)
408 411 nodeid = hex(n)
409 412 changes = cl.read(n)
410 413 p1 = cl.parents(n)[0]
411 414
412 415 files = []
413 416 mf = self.repo.manifest.read(changes[0])
414 417 for f in changes[3]:
415 418 files.append(self.t("filenodelink",
416 419 filenode=hex(mf.get(f, nullid)), file=f))
417 420
418 421 def diff(**map):
419 422 yield self.diff(p1, n, None)
420 423
421 424 yield self.t('changeset',
422 425 diff=diff,
423 426 rev=cl.rev(n),
424 427 node=nodeid,
425 parent=self.parents(n, cl.parents(n), cl.rev),
428 parent=self.siblings(cl.parents(n), cl.rev),
429 child=self.siblings(cl.children(n), cl.rev),
426 430 changesettag=self.showtag("changesettag",n),
427 431 manifest=hex(changes[0]),
428 432 author=changes[1],
429 433 desc=changes[4],
430 434 date=changes[2],
431 435 files=files,
432 436 archives=self.archivelist(nodeid))
433 437
434 438 def filelog(self, f, filenode):
435 439 cl = self.repo.changelog
436 440 fl = self.repo.file(f)
437 441 filenode = hex(fl.lookup(filenode))
438 442 count = fl.count()
439 443
440 444 def entries(**map):
441 445 l = []
442 446 parity = (count - 1) & 1
443 447
444 448 for i in range(count):
445 449 n = fl.node(i)
446 450 lr = fl.linkrev(n)
447 451 cn = cl.node(lr)
448 452 cs = cl.read(cl.node(lr))
449 453
450 454 l.insert(0, {"parity": parity,
451 455 "filenode": hex(n),
452 456 "filerev": i,
453 457 "file": f,
454 458 "node": hex(cn),
455 459 "author": cs[1],
456 460 "date": cs[2],
457 "parent": self.parents(n, fl.parents(n),
461 "parent": self.siblings(fl.parents(n),
462 fl.rev, file=f),
463 "child": self.siblings(fl.children(n),
458 464 fl.rev, file=f),
459 465 "desc": cs[4]})
460 466 parity = 1 - parity
461 467
462 468 for e in l:
463 469 yield e
464 470
465 471 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
466 472
467 473 def filerevision(self, f, node):
468 474 fl = self.repo.file(f)
469 475 n = fl.lookup(node)
470 476 node = hex(n)
471 477 text = fl.read(n)
472 478 changerev = fl.linkrev(n)
473 479 cl = self.repo.changelog
474 480 cn = cl.node(changerev)
475 481 cs = cl.read(cn)
476 482 mfn = cs[0]
477 483
478 484 mt = mimetypes.guess_type(f)[0]
479 485 rawtext = text
480 486 if util.binary(text):
481 487 text = "(binary:%s)" % mt
482 488
483 489 def lines():
484 490 for l, t in enumerate(text.splitlines(1)):
485 491 yield {"line": t,
486 492 "linenumber": "% 6d" % (l + 1),
487 493 "parity": l & 1}
488 494
489 495 yield self.t("filerevision",
490 496 file=f,
491 497 filenode=node,
492 498 path=up(f),
493 499 text=lines(),
494 500 raw=rawtext,
495 501 mimetype=mt,
496 502 rev=changerev,
497 503 node=hex(cn),
498 504 manifest=hex(mfn),
499 505 author=cs[1],
500 506 date=cs[2],
501 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
507 parent=self.siblings(fl.parents(n), fl.rev, file=f),
508 child=self.siblings(fl.children(n), fl.rev, file=f),
502 509 permissions=self.repo.manifest.readflags(mfn)[f])
503 510
504 511 def fileannotate(self, f, node):
505 512 bcache = {}
506 513 ncache = {}
507 514 fl = self.repo.file(f)
508 515 n = fl.lookup(node)
509 516 node = hex(n)
510 517 changerev = fl.linkrev(n)
511 518
512 519 cl = self.repo.changelog
513 520 cn = cl.node(changerev)
514 521 cs = cl.read(cn)
515 522 mfn = cs[0]
516 523
517 524 def annotate(**map):
518 525 parity = 1
519 526 last = None
520 527 for r, l in fl.annotate(n):
521 528 try:
522 529 cnode = ncache[r]
523 530 except KeyError:
524 531 cnode = ncache[r] = self.repo.changelog.node(r)
525 532
526 533 try:
527 534 name = bcache[r]
528 535 except KeyError:
529 536 cl = self.repo.changelog.read(cnode)
530 537 bcache[r] = name = self.repo.ui.shortuser(cl[1])
531 538
532 539 if last != cnode:
533 540 parity = 1 - parity
534 541 last = cnode
535 542
536 543 yield {"parity": parity,
537 544 "node": hex(cnode),
538 545 "rev": r,
539 546 "author": name,
540 547 "file": f,
541 548 "line": l}
542 549
543 550 yield self.t("fileannotate",
544 551 file=f,
545 552 filenode=node,
546 553 annotate=annotate,
547 554 path=up(f),
548 555 rev=changerev,
549 556 node=hex(cn),
550 557 manifest=hex(mfn),
551 558 author=cs[1],
552 559 date=cs[2],
553 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
560 parent=self.siblings(fl.parents(n), fl.rev, file=f),
561 child=self.siblings(fl.children(n), fl.rev, file=f),
554 562 permissions=self.repo.manifest.readflags(mfn)[f])
555 563
556 564 def manifest(self, mnode, path):
557 565 man = self.repo.manifest
558 566 mn = man.lookup(mnode)
559 567 mnode = hex(mn)
560 568 mf = man.read(mn)
561 569 rev = man.rev(mn)
562 570 node = self.repo.changelog.node(rev)
563 571 mff = man.readflags(mn)
564 572
565 573 files = {}
566 574
567 575 p = path[1:]
568 576 l = len(p)
569 577
570 578 for f,n in mf.items():
571 579 if f[:l] != p:
572 580 continue
573 581 remain = f[l:]
574 582 if "/" in remain:
575 583 short = remain[:remain.find("/") + 1] # bleah
576 584 files[short] = (f, None)
577 585 else:
578 586 short = os.path.basename(remain)
579 587 files[short] = (f, n)
580 588
581 589 def filelist(**map):
582 590 parity = 0
583 591 fl = files.keys()
584 592 fl.sort()
585 593 for f in fl:
586 594 full, fnode = files[f]
587 595 if not fnode:
588 596 continue
589 597
590 598 yield {"file": full,
591 599 "manifest": mnode,
592 600 "filenode": hex(fnode),
593 601 "parity": parity,
594 602 "basename": f,
595 603 "permissions": mff[full]}
596 604 parity = 1 - parity
597 605
598 606 def dirlist(**map):
599 607 parity = 0
600 608 fl = files.keys()
601 609 fl.sort()
602 610 for f in fl:
603 611 full, fnode = files[f]
604 612 if fnode:
605 613 continue
606 614
607 615 yield {"parity": parity,
608 616 "path": os.path.join(path, f),
609 617 "manifest": mnode,
610 618 "basename": f[:-1]}
611 619 parity = 1 - parity
612 620
613 621 yield self.t("manifest",
614 622 manifest=mnode,
615 623 rev=rev,
616 624 node=hex(node),
617 625 path=path,
618 626 up=up(path),
619 627 fentries=filelist,
620 628 dentries=dirlist,
621 629 archives=self.archivelist(hex(node)))
622 630
623 631 def tags(self):
624 632 cl = self.repo.changelog
625 633 mf = cl.read(cl.tip())[0]
626 634
627 635 i = self.repo.tagslist()
628 636 i.reverse()
629 637
630 638 def entries(**map):
631 639 parity = 0
632 640 for k,n in i:
633 641 yield {"parity": parity,
634 642 "tag": k,
635 643 "tagmanifest": hex(cl.read(n)[0]),
636 644 "date": cl.read(n)[2],
637 645 "node": hex(n)}
638 646 parity = 1 - parity
639 647
640 648 yield self.t("tags",
641 649 manifest=hex(mf),
642 650 entries=entries)
643 651
644 652 def summary(self):
645 653 cl = self.repo.changelog
646 654 mf = cl.read(cl.tip())[0]
647 655
648 656 i = self.repo.tagslist()
649 657 i.reverse()
650 658
651 659 def tagentries(**map):
652 660 parity = 0
653 661 count = 0
654 662 for k,n in i:
655 663 if k == "tip": # skip tip
656 664 continue;
657 665
658 666 count += 1
659 667 if count > 10: # limit to 10 tags
660 668 break;
661 669
662 670 c = cl.read(n)
663 671 m = c[0]
664 672 t = c[2]
665 673
666 674 yield self.t("tagentry",
667 675 parity = parity,
668 676 tag = k,
669 677 node = hex(n),
670 678 date = t,
671 679 tagmanifest = hex(m))
672 680 parity = 1 - parity
673 681
674 682 def changelist(**map):
675 683 parity = 0
676 684 cl = self.repo.changelog
677 685 l = [] # build a list in forward order for efficiency
678 686 for i in range(start, end):
679 687 n = cl.node(i)
680 688 changes = cl.read(n)
681 689 hn = hex(n)
682 690 t = changes[2]
683 691
684 692 l.insert(0, self.t(
685 693 'shortlogentry',
686 694 parity = parity,
687 695 author = changes[1],
688 696 manifest = hex(changes[0]),
689 697 desc = changes[4],
690 698 date = t,
691 699 rev = i,
692 700 node = hn))
693 701 parity = 1 - parity
694 702
695 703 yield l
696 704
697 705 cl = self.repo.changelog
698 706 mf = cl.read(cl.tip())[0]
699 707 count = cl.count()
700 708 start = max(0, count - self.maxchanges)
701 709 end = min(count, start + self.maxchanges)
702 710 pos = end - 1
703 711
704 712 yield self.t("summary",
705 713 desc = self.repo.ui.config("web", "description", "unknown"),
706 714 owner = (self.repo.ui.config("ui", "username") or # preferred
707 715 self.repo.ui.config("web", "contact") or # deprecated
708 716 self.repo.ui.config("web", "author", "unknown")), # also
709 717 lastchange = (0, 0), # FIXME
710 718 manifest = hex(mf),
711 719 tags = tagentries,
712 720 shortlog = changelist)
713 721
714 722 def filediff(self, file, changeset):
715 723 cl = self.repo.changelog
716 724 n = self.repo.lookup(changeset)
717 725 changeset = hex(n)
718 726 p1 = cl.parents(n)[0]
719 727 cs = cl.read(n)
720 728 mf = self.repo.manifest.read(cs[0])
721 729
722 730 def diff(**map):
723 731 yield self.diff(p1, n, file)
724 732
725 733 yield self.t("filediff",
726 734 file=file,
727 735 filenode=hex(mf.get(file, nullid)),
728 736 node=changeset,
729 737 rev=self.repo.changelog.rev(n),
730 parent=self.parents(n, cl.parents(n), cl.rev),
738 parent=self.siblings(cl.parents(n), cl.rev),
739 child=self.siblings(cl.children(n), cl.rev),
731 740 diff=diff)
732 741
733 742 def archive(self, req, cnode, type):
734 743 cs = self.repo.changelog.read(cnode)
735 744 mnode = cs[0]
736 745 mf = self.repo.manifest.read(mnode)
737 746 rev = self.repo.manifest.rev(mnode)
738 747 reponame = re.sub(r"\W+", "-", self.reponame)
739 748 name = "%s-%s/" % (reponame, short(cnode))
740 749
741 750 files = mf.keys()
742 751 files.sort()
743 752
744 753 if type == 'zip':
745 754 tmp = tempfile.mkstemp()[1]
746 755 try:
747 756 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
748 757
749 758 for f in files:
750 759 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
751 760 zf.close()
752 761
753 762 f = open(tmp, 'r')
754 763 req.httphdr('application/zip', name[:-1] + '.zip',
755 764 os.path.getsize(tmp))
756 765 req.write(f.read())
757 766 f.close()
758 767 finally:
759 768 os.unlink(tmp)
760 769
761 770 else:
762 771 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
763 772 mff = self.repo.manifest.readflags(mnode)
764 773 mtime = int(time.time())
765 774
766 775 if type == "gz":
767 776 encoding = "gzip"
768 777 else:
769 778 encoding = "x-bzip2"
770 779 req.header([('Content-type', 'application/x-tar'),
771 780 ('Content-disposition', 'attachment; filename=%s%s%s' %
772 781 (name[:-1], '.tar.', type)),
773 782 ('Content-encoding', encoding)])
774 783 for fname in files:
775 784 rcont = self.repo.file(fname).read(mf[fname])
776 785 finfo = tarfile.TarInfo(name + fname)
777 786 finfo.mtime = mtime
778 787 finfo.size = len(rcont)
779 788 finfo.mode = mff[fname] and 0755 or 0644
780 789 tf.addfile(finfo, StringIO.StringIO(rcont))
781 790 tf.close()
782 791
783 792 # add tags to things
784 793 # tags -> list of changesets corresponding to tags
785 794 # find tag, changeset, file
786 795
787 796 def run(self, req=hgrequest()):
788 797 def header(**map):
789 798 yield self.t("header", **map)
790 799
791 800 def footer(**map):
792 801 yield self.t("footer", **map)
793 802
794 803 def expand_form(form):
795 804 shortcuts = {
796 805 'cl': [('cmd', ['changelog']), ('rev', None)],
797 806 'cs': [('cmd', ['changeset']), ('node', None)],
798 807 'f': [('cmd', ['file']), ('filenode', None)],
799 808 'fl': [('cmd', ['filelog']), ('filenode', None)],
800 809 'fd': [('cmd', ['filediff']), ('node', None)],
801 810 'fa': [('cmd', ['annotate']), ('filenode', None)],
802 811 'mf': [('cmd', ['manifest']), ('manifest', None)],
803 812 'ca': [('cmd', ['archive']), ('node', None)],
804 813 'tags': [('cmd', ['tags'])],
805 814 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
806 815 }
807 816
808 817 for k in shortcuts.iterkeys():
809 818 if form.has_key(k):
810 819 for name, value in shortcuts[k]:
811 820 if value is None:
812 821 value = form[k]
813 822 form[name] = value
814 823 del form[k]
815 824
816 825 self.refresh()
817 826
818 827 expand_form(req.form)
819 828
820 829 t = self.repo.ui.config("web", "templates", templatepath())
821 830 m = os.path.join(t, "map")
822 831 style = self.repo.ui.config("web", "style", "")
823 832 if req.form.has_key('style'):
824 833 style = req.form['style'][0]
825 834 if style:
826 835 b = os.path.basename("map-" + style)
827 836 p = os.path.join(t, b)
828 837 if os.path.isfile(p):
829 838 m = p
830 839
831 840 port = req.env["SERVER_PORT"]
832 841 port = port != "80" and (":" + port) or ""
833 842 uri = req.env["REQUEST_URI"]
834 843 if "?" in uri:
835 844 uri = uri.split("?")[0]
836 845 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
837 846 if not self.reponame:
838 847 self.reponame = (self.repo.ui.config("web", "name")
839 848 or uri.strip('/') or self.repo.root)
840 849
841 850 self.t = templater(m, common_filters,
842 851 {"url": url,
843 852 "repo": self.reponame,
844 853 "header": header,
845 854 "footer": footer,
846 855 })
847 856
848 857 if not req.form.has_key('cmd'):
849 858 req.form['cmd'] = [self.t.cache['default'],]
850 859
851 860 if req.form['cmd'][0] == 'changelog':
852 861 c = self.repo.changelog.count() - 1
853 862 hi = c
854 863 if req.form.has_key('rev'):
855 864 hi = req.form['rev'][0]
856 865 try:
857 866 hi = self.repo.changelog.rev(self.repo.lookup(hi))
858 867 except hg.RepoError:
859 868 req.write(self.search(hi))
860 869 return
861 870
862 871 req.write(self.changelog(hi))
863 872
864 873 elif req.form['cmd'][0] == 'changeset':
865 874 req.write(self.changeset(req.form['node'][0]))
866 875
867 876 elif req.form['cmd'][0] == 'manifest':
868 877 req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
869 878
870 879 elif req.form['cmd'][0] == 'tags':
871 880 req.write(self.tags())
872 881
873 882 elif req.form['cmd'][0] == 'summary':
874 883 req.write(self.summary())
875 884
876 885 elif req.form['cmd'][0] == 'filediff':
877 886 req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
878 887
879 888 elif req.form['cmd'][0] == 'file':
880 889 req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
881 890
882 891 elif req.form['cmd'][0] == 'annotate':
883 892 req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
884 893
885 894 elif req.form['cmd'][0] == 'filelog':
886 895 req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
887 896
888 897 elif req.form['cmd'][0] == 'heads':
889 898 req.httphdr("application/mercurial-0.1")
890 899 h = self.repo.heads()
891 900 req.write(" ".join(map(hex, h)) + "\n")
892 901
893 902 elif req.form['cmd'][0] == 'branches':
894 903 req.httphdr("application/mercurial-0.1")
895 904 nodes = []
896 905 if req.form.has_key('nodes'):
897 906 nodes = map(bin, req.form['nodes'][0].split(" "))
898 907 for b in self.repo.branches(nodes):
899 908 req.write(" ".join(map(hex, b)) + "\n")
900 909
901 910 elif req.form['cmd'][0] == 'between':
902 911 req.httphdr("application/mercurial-0.1")
903 912 nodes = []
904 913 if req.form.has_key('pairs'):
905 914 pairs = [map(bin, p.split("-"))
906 915 for p in req.form['pairs'][0].split(" ")]
907 916 for b in self.repo.between(pairs):
908 917 req.write(" ".join(map(hex, b)) + "\n")
909 918
910 919 elif req.form['cmd'][0] == 'changegroup':
911 920 req.httphdr("application/mercurial-0.1")
912 921 nodes = []
913 922 if not self.allowpull:
914 923 return
915 924
916 925 if req.form.has_key('roots'):
917 926 nodes = map(bin, req.form['roots'][0].split(" "))
918 927
919 928 z = zlib.compressobj()
920 929 f = self.repo.changegroup(nodes)
921 930 while 1:
922 931 chunk = f.read(4096)
923 932 if not chunk:
924 933 break
925 934 req.write(z.compress(chunk))
926 935
927 936 req.write(z.flush())
928 937
929 938 elif req.form['cmd'][0] == 'archive':
930 939 changeset = self.repo.lookup(req.form['node'][0])
931 940 type = req.form['type'][0]
932 941 if (type in self.archives and
933 942 self.repo.ui.configbool("web", "allow" + type, False)):
934 943 self.archive(req, changeset, type)
935 944 return
936 945
937 946 req.write(self.t("error"))
938 947
939 948 else:
940 949 req.write(self.t("error"))
941 950
942 951 def create_server(repo):
943 952
944 953 def openlog(opt, default):
945 954 if opt and opt != '-':
946 955 return open(opt, 'w')
947 956 return default
948 957
949 958 address = repo.ui.config("web", "address", "")
950 959 port = int(repo.ui.config("web", "port", 8000))
951 960 use_ipv6 = repo.ui.configbool("web", "ipv6")
952 961 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
953 962 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
954 963
955 964 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
956 965 address_family = getattr(socket, 'AF_INET6', None)
957 966
958 967 def __init__(self, *args, **kwargs):
959 968 if self.address_family is None:
960 969 raise hg.RepoError(_('IPv6 not available on this system'))
961 970 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
962 971
963 972 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
964 973 def log_error(self, format, *args):
965 974 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
966 975 self.log_date_time_string(),
967 976 format % args))
968 977
969 978 def log_message(self, format, *args):
970 979 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
971 980 self.log_date_time_string(),
972 981 format % args))
973 982
974 983 def do_POST(self):
975 984 try:
976 985 self.do_hgweb()
977 986 except socket.error, inst:
978 987 if inst[0] != errno.EPIPE:
979 988 raise
980 989
981 990 def do_GET(self):
982 991 self.do_POST()
983 992
984 993 def do_hgweb(self):
985 994 query = ""
986 995 p = self.path.find("?")
987 996 if p:
988 997 query = self.path[p + 1:]
989 998 query = query.replace('+', ' ')
990 999
991 1000 env = {}
992 1001 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
993 1002 env['REQUEST_METHOD'] = self.command
994 1003 env['SERVER_NAME'] = self.server.server_name
995 1004 env['SERVER_PORT'] = str(self.server.server_port)
996 1005 env['REQUEST_URI'] = "/"
997 1006 if query:
998 1007 env['QUERY_STRING'] = query
999 1008 host = self.address_string()
1000 1009 if host != self.client_address[0]:
1001 1010 env['REMOTE_HOST'] = host
1002 1011 env['REMOTE_ADDR'] = self.client_address[0]
1003 1012
1004 1013 if self.headers.typeheader is None:
1005 1014 env['CONTENT_TYPE'] = self.headers.type
1006 1015 else:
1007 1016 env['CONTENT_TYPE'] = self.headers.typeheader
1008 1017 length = self.headers.getheader('content-length')
1009 1018 if length:
1010 1019 env['CONTENT_LENGTH'] = length
1011 1020 accept = []
1012 1021 for line in self.headers.getallmatchingheaders('accept'):
1013 1022 if line[:1] in "\t\n\r ":
1014 1023 accept.append(line.strip())
1015 1024 else:
1016 1025 accept = accept + line[7:].split(',')
1017 1026 env['HTTP_ACCEPT'] = ','.join(accept)
1018 1027
1019 1028 req = hgrequest(self.rfile, self.wfile, env)
1020 1029 self.send_response(200, "Script output follows")
1021 1030 hg.run(req)
1022 1031
1023 1032 hg = hgweb(repo)
1024 1033 if use_ipv6:
1025 1034 return IPv6HTTPServer((address, port), hgwebhandler)
1026 1035 else:
1027 1036 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
1028 1037
1029 1038 # This is a stopgap
1030 1039 class hgwebdir(object):
1031 1040 def __init__(self, config):
1032 1041 def cleannames(items):
1033 1042 return [(name.strip('/'), path) for name, path in items]
1034 1043
1035 1044 if type(config) == type([]):
1036 1045 self.repos = cleannames(config)
1037 1046 elif type(config) == type({}):
1038 1047 self.repos = cleannames(config.items())
1039 1048 self.repos.sort()
1040 1049 else:
1041 1050 cp = ConfigParser.SafeConfigParser()
1042 1051 cp.read(config)
1043 1052 self.repos = cleannames(cp.items("paths"))
1044 1053 self.repos.sort()
1045 1054
1046 1055 def run(self, req=hgrequest()):
1047 1056 def header(**map):
1048 1057 yield tmpl("header", **map)
1049 1058
1050 1059 def footer(**map):
1051 1060 yield tmpl("footer", **map)
1052 1061
1053 1062 m = os.path.join(templatepath(), "map")
1054 1063 tmpl = templater(m, common_filters,
1055 1064 {"header": header, "footer": footer})
1056 1065
1057 1066 def entries(**map):
1058 1067 parity = 0
1059 1068 for name, path in self.repos:
1060 1069 u = ui.ui()
1061 1070 try:
1062 1071 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
1063 1072 except IOError:
1064 1073 pass
1065 1074 get = u.config
1066 1075
1067 1076 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
1068 1077 .replace("//", "/"))
1069 1078
1070 1079 # update time with local timezone
1071 1080 try:
1072 1081 d = (get_mtime(path), util.makedate()[1])
1073 1082 except OSError:
1074 1083 continue
1075 1084
1076 1085 yield dict(contact=(get("ui", "username") or # preferred
1077 1086 get("web", "contact") or # deprecated
1078 1087 get("web", "author", "unknown")), # also
1079 1088 name=get("web", "name", name),
1080 1089 url=url,
1081 1090 parity=parity,
1082 1091 shortdesc=get("web", "description", "unknown"),
1083 1092 lastupdate=d)
1084 1093
1085 1094 parity = 1 - parity
1086 1095
1087 1096 virtual = req.env.get("PATH_INFO", "").strip('/')
1088 1097 if virtual:
1089 1098 real = dict(self.repos).get(virtual)
1090 1099 if real:
1091 1100 try:
1092 1101 hgweb(real).run(req)
1093 1102 except IOError, inst:
1094 1103 req.write(tmpl("error", error=inst.strerror))
1095 1104 except hg.RepoError, inst:
1096 1105 req.write(tmpl("error", error=str(inst)))
1097 1106 else:
1098 1107 req.write(tmpl("notfound", repo=virtual))
1099 1108 else:
1100 1109 req.write(tmpl("index", entries=entries))
@@ -1,24 +1,25 b''
1 1 <table class="changelogEntry parity#parity#">
2 2 <tr>
3 3 <th class="age" width="15%">#date|age# ago:</th>
4 4 <th class="firstline">#desc|strip|firstline|escape#</th>
5 5 </tr>
6 6 <tr>
7 7 <th class="changesetRev">changeset #rev#:</th>
8 8 <td class="changesetNode"><a href="?cs=#node|short#">#node|short#</a></td>
9 9 </tr>
10 10 #parent%changelogparent#
11 #child%changelogchild#
11 12 #changelogtag#
12 13 <tr>
13 14 <th class="author">author:</th>
14 15 <td class="author">#author|obfuscate#</td>
15 16 </tr>
16 17 <tr>
17 18 <th class="date">date:</th>
18 19 <td class="date">#date|date#</td>
19 20 </tr>
20 21 <tr>
21 22 <th class="files"><a href="?mf=#manifest|short#;path=/">files</a>:</th>
22 23 <td class="files">#files#</td>
23 24 </tr>
24 25 </table>
@@ -1,40 +1,41 b''
1 1 #header#
2 2 <title>#repo|escape#: Changeset</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 5 </head>
6 6 <body>
7 7
8 8 <div class="page_header">
9 9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / changeset
10 10 </div>
11 11
12 12 <div class="page_nav">
13 13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> | changeset | <a href="?cmd=changeset;node=#node#;style=raw">raw</a><br/>
14 14 </div>
15 15
16 16 <div>
17 17 <a class="title" href="?cmd=changeset;node=#node#;style=raw">#desc|escape|firstline#</a>
18 18 </div>
19 19 <div class="title_text">
20 20 <table cellspacing="0">
21 21 <tr><td>author</td><td>#author|obfuscate#</td></tr>
22 22 <tr><td></td><td>#date|date# (#date|age# ago)</td></tr>
23 23 <tr><td>changeset</td><td style="font-family:monospace">#node|short#</td></tr>
24 24 <tr><td>manifest</td><td style="font-family:monospace"><a class="list" href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">#manifest|short#</a></td></tr>
25 25 #parent%changesetparent#
26 #child%changesetchild#
26 27 #changesettag#
27 28 </table></div>
28 29
29 30 <div class="title_text">
30 31 #desc|addbreaks#
31 32 </div>
32 33
33 34 <div class="title_text">
34 35 <table cellspacing="0">
35 36 #files#
36 37 </table></div>
37 38
38 39 <div class="page_body">#diff#</div>
39 40
40 41 #footer#
@@ -1,45 +1,46 b''
1 1 #header#
2 2 <title>#repo|escape#: changeset #node|short#</title>
3 3 </head>
4 4 <body>
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 8 <a href="?cmd=tags">tags</a>
9 9 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 10 <a href="?cs=#node|short#;style=raw">raw</a>
11 11 #archives%archiveentry#
12 12 </div>
13 13
14 14 <h2>changeset: #desc|strip|escape|firstline#</h2>
15 15
16 16 <table id="changesetEntry">
17 17 <tr>
18 18 <th class="changeset">changeset #rev#:</th>
19 19 <td class="changeset"><a href="?cs=#node|short#">#node|short#</a></td>
20 20 </tr>
21 21 #parent%changesetparent#
22 #child%changesetchild#
22 23 #changesettag#
23 24 <tr>
24 25 <th class="author">author:</th>
25 26 <td class="author">#author|obfuscate#</td>
26 27 </tr>
27 28 <tr>
28 29 <th class="date">date:</th>
29 30 <td class="date">#date|date# (#date|age# ago)</td></tr>
30 31 <tr>
31 32 <th class="files">files:</th>
32 33 <td class="files">#files#</td></tr>
33 34 <tr>
34 35 <th class="description">description:</th>
35 36 <td class="description">#desc|strip|escape|addbreaks#</td>
36 37 </tr>
37 38 </table>
38 39
39 40 <div id="changesetDiff">
40 41 #diff#
41 42 </div>
42 43
43 44 #footer#
44 45
45 46
@@ -1,43 +1,44 b''
1 1 #header#
2 2 <title>#repo|escape#: Annotate</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 5 </head>
6 6 <body>
7 7
8 8 <div class="page_header">
9 9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / annotate
10 10 </div>
11 11
12 12 <div class="page_nav">
13 13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=#path#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | <a href="?cmd=file;file=#file#;filenode=#filenode#;style=gitweb">file</a> | <a href="?cmd=filelog;file=#file#;filenode=#filenode#;style=gitweb">revisions</a> | annotate<br/>
14 14 </div>
15 15
16 16 <div class="title">#file#</div>
17 17
18 18 <table>
19 19 <tr>
20 20 <td class="metatag">changeset #rev#:</td>
21 21 <td><a href="?cs=#node|short#;style=gitweb">#node|short#</a></td></tr>
22 22 #parent%fileannotateparent#
23 #child%fileannotatechild#
23 24 <tr>
24 25 <td class="metatag">manifest:</td>
25 26 <td><a href="?mf=#manifest|short#;path=/;style=gitweb">#manifest|short#</a></td></tr>
26 27 <tr>
27 28 <td class="metatag">author:</td>
28 29 <td>#author|obfuscate#</td></tr>
29 30 <tr>
30 31 <td class="metatag">date:</td>
31 32 <td>#date|date# (#date|age# ago)</td></tr>
32 33 <tr>
33 34 <td class="metatag">permissions:</td>
34 35 <td>#permissions|permissions#</td></tr>
35 36 </table>
36 37
37 38 <div class="page_body">
38 39 <table>
39 40 #annotate%annotateline#
40 41 </table>
41 42 </div>
42 43
43 44 #footer#
@@ -1,39 +1,40 b''
1 1 #header#
2 2 <title>#repo|escape#: #file# annotate</title>
3 3 </head>
4 4 <body>
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 8 <a href="?tags=">tags</a>
9 9 <a href="?cs=#node|short#">changeset</a>
10 10 <a href="?mf=#manifest|short#;path=#path#">manifest</a>
11 11 <a href="?f=#filenode|short#;file=#file#">file</a>
12 12 <a href="?fl=#filenode|short#;file=#file#">revisions</a>
13 13 </div>
14 14
15 15 <h2>Annotate #file#</h2>
16 16
17 17 <table>
18 18 <tr>
19 19 <td class="metatag">changeset #rev#:</td>
20 20 <td><a href="?cs=#node|short#">#node|short#</a></td></tr>
21 21 #parent%fileannotateparent#
22 #child%fileannotatechild#
22 23 <tr>
23 24 <td class="metatag">author:</td>
24 25 <td>#author|obfuscate#</td></tr>
25 26 <tr>
26 27 <td class="metatag">date:</td>
27 28 <td>#date|date# (#date|age# ago)</td></tr>
28 29 <tr>
29 30 <td class="metatag">permissions:</td>
30 31 <td>#permissions|permissions#</td></tr>
31 32 </table>
32 33
33 34 <br/>
34 35
35 36 <table cellspacing="0" cellpadding="0">
36 37 #annotate%annotateline#
37 38 </table>
38 39
39 40 #footer#
@@ -1,32 +1,33 b''
1 1 #header#
2 2 <title>#repo|escape#: #file# diff</title>
3 3 </head>
4 4 <body>
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 8 <a href="?tags=">tags</a>
9 9 <a href="?cs=#node|short#">changeset</a>
10 10 <a href="?f=#filenode|short#;file=#file#">file</a>
11 11 <a href="?fl=#filenode|short#;file=#file#">revisions</a>
12 12 <a href="?fa=#filenode|short#;file=#file#">annotate</a>
13 13 <a href="?fd=#node|short#;file=#file#;style=raw">raw</a>
14 14 </div>
15 15
16 16 <h2>#file#</h2>
17 17
18 18 <table id="filediffEntry">
19 19 <tr>
20 20 <th class="revision">revision #rev#:</th>
21 21 <td class="revision"><a href="?cs=#node|short#">#node|short#</a></td>
22 22 </tr>
23 23 #parent%filediffparent#
24 #child%filediffchild#
24 25 </table>
25 26
26 27 <div id="fileDiff">
27 28 #diff#
28 29 </div>
29 30
30 31 #footer#
31 32
32 33
@@ -1,41 +1,42 b''
1 1 #header#
2 2 <title>#repo|escape#: File revision</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 5 </head>
6 6 <body>
7 7
8 8 <div class="page_header">
9 9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / file revision
10 10 </div>
11 11
12 12 <div class="page_nav">
13 13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?mf=#manifest|short#;path=#path#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | file | <a href="?cmd=filelog;file=#file#;filenode=#filenode#;style=gitweb">revisions</a> | <a href="?cmd=annotate;file=#file#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?cmd=file;file=#file#;filenode=#filenode#;style=raw">raw</a><br/>
14 14 </div>
15 15
16 16 <div class="title">#file#</div>
17 17
18 18 <table>
19 19 <tr>
20 20 <td class="metatag">changeset #rev#:</td>
21 21 <td><a href="?cs=#node|short#;style=gitweb">#node|short#</a></td></tr>
22 22 #parent%fileannotateparent#
23 #child%fileannotatechild#
23 24 <tr>
24 25 <td class="metatag">manifest:</td>
25 26 <td><a href="?mf=#manifest|short#;path=/;style=gitweb">#manifest|short#</a></td></tr>
26 27 <tr>
27 28 <td class="metatag">author:</td>
28 29 <td>#author|obfuscate#</td></tr>
29 30 <tr>
30 31 <td class="metatag">date:</td>
31 32 <td>#date|date# (#date|age# ago)</td></tr>
32 33 <tr>
33 34 <td class="metatag">permissions:</td>
34 35 <td>#permissions|permissions#</td></tr>
35 36 </table>
36 37
37 38 <div class="page_body">
38 39 #text%fileline#
39 40 </div>
40 41
41 42 #footer#
@@ -1,38 +1,39 b''
1 1 #header#
2 2 <title>#repo|escape#:#file#</title>
3 3 </head>
4 4 <body>
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 8 <a href="?tags=">tags</a>
9 9 <a href="?cs=#node|short#">changeset</a>
10 10 <a href="?mf=#manifest|short#;path=#path#">manifest</a>
11 11 <a href="?fl=#filenode|short#;file=#file#">revisions</a>
12 12 <a href="?fa=#filenode|short#;file=#file#">annotate</a>
13 13 <a href="?f=#filenode|short#;file=#file#;style=raw">raw</a>
14 14 </div>
15 15
16 16 <h2>#file#</h2>
17 17
18 18 <table>
19 19 <tr>
20 20 <td class="metatag">changeset #rev#:</td>
21 21 <td><a href="?cs=#node|short#">#node|short#</a></td></tr>
22 22 #parent%filerevparent#
23 #child%filerevchild#
23 24 <tr>
24 25 <td class="metatag">author:</td>
25 26 <td>#author|obfuscate#</td></tr>
26 27 <tr>
27 28 <td class="metatag">date:</td>
28 29 <td>#date|date# (#date|age# ago)</td></tr>
29 30 <tr>
30 31 <td class="metatag">permissions:</td>
31 32 <td>#permissions|permissions#</td></tr>
32 33 </table>
33 34
34 35 <pre>
35 36 #text%fileline#
36 37 </pre>
37 38
38 39 #footer#
@@ -1,42 +1,48 b''
1 1 default = "changelog"
2 2 header = header.tmpl
3 3 footer = footer.tmpl
4 4 search = search.tmpl
5 5 changelog = changelog.tmpl
6 6 naventry = "<a href="?cl=#rev#">#label#</a> "
7 7 filedifflink = "<a href="?fd=#node|short#;file=#file#">#file#</a> "
8 8 filenodelink = "<a href="?f=#filenode|short#;file=#file#">#file#</a> "
9 9 fileellipses = "..."
10 10 changelogentry = changelogentry.tmpl
11 11 searchentry = changelogentry.tmpl
12 12 changeset = changeset.tmpl
13 13 manifest = manifest.tmpl
14 14 manifestdirentry = "<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td><a href="?cmd=manifest;manifest=#manifest#;path=#path#">#basename#/</a>"
15 15 manifestfileentry = "<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td><a href="?f=#filenode|short#;file=#file#">#basename#</a>"
16 16 filerevision = filerevision.tmpl
17 17 fileannotate = fileannotate.tmpl
18 18 filediff = filediff.tmpl
19 19 filelog = filelog.tmpl
20 20 fileline = "<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>"
21 21 filelogentry = filelogentry.tmpl
22 22 annotateline = "<tr class="parity#parity#"><td class="annotate"><a href="?cs=#node|short#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>"
23 23 difflineplus = "<span class="plusline">#line|escape#</span>"
24 24 difflineminus = "<span class="minusline">#line|escape#</span>"
25 25 difflineat = "<span class="atline">#line|escape#</span>"
26 26 diffline = "#line|escape#"
27 27 changelogparent = "<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>"
28 28 changesetparent = "<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>"
29 29 filerevparent = "<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file#">#node|short#</a></td></tr>"
30 30 fileannotateparent = "<tr><td class="metatag">parent:</td><td><a href="?fa=#filenode|short#;file=#file#">#node|short#</a></td></tr>"
31 changesetchild = "<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>"
32 changelogchild = "<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>"
33 filerevchild = "<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file#">#node|short#</a></td></tr>"
34 fileannotatechild = "<tr><td class="metatag">child:</td><td><a href="?fa=#filenode|short#;file=#file#">#node|short#</a></td></tr>"
31 35 tags = tags.tmpl
32 36 tagentry = "<li class="tagEntry parity#parity#"><span class="node">#node#</span> <a href="?cs=#node|short#">#tag#</a></li>"
33 37 diffblock = "<pre class="parity#parity#">#lines#</pre>"
34 38 changelogtag = "<tr><th class="tag">tag:</th><td class="tag">#tag#</td></tr>"
35 39 changesettag = "<tr><th class="tag">tag:</th><td class="tag">#tag#</td></tr>"
36 40 filediffparent = "<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cs=#node|short#">#node|short#</a></td></tr>"
37 41 filelogparent = "<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="?f=#node|short#;file=#file#">#node|short#</a></td></tr>"
42 filediffchild = "<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>"
43 filelogchild = "<tr><td align="right">child #rev#:&nbsp;</td><td><a href="?f=#node|short#;file=#file#">#node|short#</a></td></tr>"
38 44 indexentry = "<tr class="parity#parity#"><td><a href="#url#">#name#</a></td><td>#shortdesc#</td><td>#contact|obfuscate#</td><td>#lastupdate|age# ago</td><td><a href="#url#?cl=tip;style=rss">RSS</a></td></tr>"
39 45 index = index.tmpl
40 46 archiveentry = "<a href="?ca=#node|short#;type=#type#">#type#</a> "
41 47 notfound = notfound.tmpl
42 48 error = error.tmpl
@@ -1,42 +1,48 b''
1 1 default = "summary"
2 2 header = header-gitweb.tmpl
3 3 footer = footer-gitweb.tmpl
4 4 search = search-gitweb.tmpl
5 5 changelog = changelog-gitweb.tmpl
6 6 summary = summary-gitweb.tmpl
7 7 error = error-gitweb.tmpl
8 8 naventry = "<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label#</a> "
9 9 navshortentry = "<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label#</a> "
10 10 filedifflink = "<a href="?cmd=filediff;node=#node#;file=#file#;style=gitweb">#file#</a> "
11 11 filenodelink = "<tr class="light"><td><a class="list" href="">#file#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file#;style=gitweb">file</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file#;style=gitweb">revisions</a></td></tr>"
12 12 fileellipses = "..."
13 13 changelogentry = changelogentry-gitweb.tmpl
14 14 searchentry = changelogentry-gitweb.tmpl
15 15 changeset = changeset-gitweb.tmpl
16 16 manifest = manifest-gitweb.tmpl
17 17 manifestdirentry = "<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td><a href="?mf=#manifest|short#;path=#path#;style=gitweb">#basename#/</a></td><td class="link"><a href="?mf=#manifest|short#;path=#path#;style=gitweb">manifest</a></td></tr>"
18 18 manifestfileentry = "<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td class="list"><a class="list" href="?f=#filenode|short#;file=#file#;style=gitweb">#basename#</a></td><td class="link"><a href="?f=#filenode|short#;file=#file#;style=gitweb">file</a> | <a href="?fl=#filenode|short#;file=#file#;style=gitweb">revisions</a> | <a href="?fa=#filenode|short#;file=#file#;style=gitweb">annotate</a></td></tr>"
19 19 filerevision = filerevision-gitweb.tmpl
20 20 fileannotate = fileannotate-gitweb.tmpl
21 21 filelog = filelog-gitweb.tmpl
22 22 fileline = "<div style="font-family:monospace; white-space: pre;" class="parity#parity#"><span class="linenr"> #linenumber#</span> #line|escape#</div>"
23 23 filelogentry = filelogentry-gitweb.tmpl
24 24 annotateline = "<tr style="font-family:monospace; white-space: pre;" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="?cs=#node|short#;style=gitweb">#author|obfuscate#@#rev#</a></td><td>#line|escape#</td></tr>"
25 25 difflineplus = "<div class="pre" style="color:#008800;">#line|escape#</div>"
26 26 difflineminus = "<div class="pre" style="color:#cc0000;">#line|escape#</div>"
27 27 difflineat = "<div class="pre" style="color:#990099;">#line|escape#</div>"
28 28 diffline = "<div class="pre">#line|escape#</div>"
29 29 changelogparent = "<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>"
30 30 changesetparent = "<tr><td>parent</td><td style="font-family:monospace"><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb">#node|short#</a></td></tr>"
31 31 filerevparent = "<tr><td class="metatag">parent:</td><td><a href="?cmd=file;file=#file#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>"
32 32 fileannotateparent = "<tr><td class="metatag">parent:</td><td><a href="?cmd=annotate;file=#file#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>"
33 changelogchild = "<tr><th class="child">child #rev#:</th><td class="child"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>"
34 changesetchild = "<tr><td>child</td><td style="font-family:monospace"><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb">#node|short#</a></td></tr>"
35 filerevchild = "<tr><td class="metatag">child:</td><td><a href="?cmd=file;file=#file#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>"
36 fileannotatechild = "<tr><td class="metatag">child:</td><td><a href="?cmd=annotate;file=#file#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>"
33 37 tags = tags-gitweb.tmpl
34 38 tagentry = "<tr class="parity#parity#"><td><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#tag#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=changelog;rev=#node|short#;style=gitweb">changelog</a> | <a href="?mf=#tagmanifest|short#;path=/;style=gitweb">manifest</a></td></tr>"
35 39 diffblock = "#lines#"
36 40 changelogtag = "<tr><th class="tag">tag:</th><td class="tag">#tag#</td></tr>"
37 41 changesettag = "<tr><td>tag</td><td>#tag#</td></tr>"
38 42 filediffparent = "<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>"
39 43 filelogparent = "<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>"
44 filediffchild = "<tr><th class="child">child #rev#:</th><td class="child"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>"
45 filelogchild = "<tr><td align="right">child #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>"
40 46 shortlog = shortlog-gitweb.tmpl
41 47 shortlogentry = "<tr class="parity#parity#"><td><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>"
42 48 filelogentry = "<tr class="parity#parity#"><td><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|firstline|escape#</b></a></td><td class="link"><!-- FIXME: <a href="?fd=#node|short#;file=#file#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file#;style=gitweb">annotate</a></td></tr>"
@@ -1,14 +1,15 b''
1 1 header = header-raw.tmpl
2 2 footer = ""
3 3 changeset = changeset-raw.tmpl
4 4 annotateline = "<tr class="parity#parity#"><td class="annotate"><a href="?cmd=changeset;node=#node#">#author#@#rev#</a></td><td><pre>#line#</pre></td></tr>"
5 5 difflineplus = "#line#"
6 6 difflineminus = "#line#"
7 7 difflineat = "#line#"
8 8 diffline = "#line#"
9 9 changesetparent = "# parent: #node#"
10 changesetchild = "# child: #node#"
10 11 filenodelink = "#file#"
11 12 filerevision = filerevision-raw.tmpl
12 13 fileline = "#line#"
13 14 diffblock = "#lines#"
14 15 filediff = filediff-raw.tmpl
@@ -1,38 +1,39 b''
1 1 repo the name of the repo
2 2 rev a changeset.manifest revision
3 3 node a changeset node
4 4 changesets total number of changesets
5 5 file a filename
6 6 filenode a file node
7 7 filerev a file revision
8 8 filerevs total number of file revisions
9 9 up the directory of the relevant file
10 10 path a path in the manifest, starting with "/"
11 11 basename a short pathname
12 12 manifest a manifest node
13 13 manifestrev a manifest revision
14 14 date a date string
15 15 age age in hours, days, etc
16 16 line a line of text (escaped)
17 17 desc a description (escaped, with breaks)
18 18 shortdesc a short description (escaped)
19 19 author a name or email addressv(obfuscated)
20 20 parent a list of the parent
21 child a list of the children
21 22 tags a list of tag
22 23
23 24 header the global page header
24 25 footer the global page footer
25 26
26 27 files a list of file links
27 28 dirs a set of directory links
28 29 diff a diff of one or more files
29 30 annotate an annotated file
30 31 entries the entries relevant to the page
31 32
32 33 Templates and commands:
33 34 changelog(rev) - a page for browsing changesets
34 35 naventry - a link for jumping to a changeset number
35 36 filenodelink - jump to file diff
36 37 fileellipses - printed after maxfiles
37 38 changelogentry - an entry in the log
38 39 manifest - browse a manifest as a directory tree No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now