##// END OF EJS Templates
hgweb: Keep session variables (currently only style) in HTML forms, too....
Thomas Arendsen Hein -
r3362:887da224 default
parent child Browse files
Show More
@@ -1,1047 +1,1058 b''
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 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
10 10 import os.path
11 11 import mimetypes
12 12 from mercurial.demandload import demandload
13 13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 14 demandload(globals(), 'urllib')
15 15 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
16 16 demandload(globals(), "mercurial:revlog,templater")
17 17 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile,style_map")
18 18 from mercurial.node import *
19 19 from mercurial.i18n import gettext as _
20 20
21 21 def _up(p):
22 22 if p[0] != "/":
23 23 p = "/" + p
24 24 if p[-1] == "/":
25 25 p = p[:-1]
26 26 up = os.path.dirname(p)
27 27 if up == "/":
28 28 return "/"
29 29 return up + "/"
30 30
31 31 class hgweb(object):
32 32 def __init__(self, repo, name=None):
33 33 if type(repo) == type(""):
34 34 self.repo = hg.repository(ui.ui(), repo)
35 35 else:
36 36 self.repo = repo
37 37
38 38 self.mtime = -1
39 39 self.reponame = name
40 40 self.archives = 'zip', 'gz', 'bz2'
41 41 self.stripecount = 1
42 42 self.templatepath = self.repo.ui.config("web", "templates",
43 43 templater.templatepath())
44 44
45 45 def refresh(self):
46 46 mtime = get_mtime(self.repo.root)
47 47 if mtime != self.mtime:
48 48 self.mtime = mtime
49 49 self.repo = hg.repository(self.repo.ui, self.repo.root)
50 50 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
51 51 self.stripecount = int(self.repo.ui.config("web", "stripes", 1))
52 52 self.maxshortchanges = int(self.repo.ui.config("web", "maxshortchanges", 60))
53 53 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
54 54 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
55 55
56 56 def archivelist(self, nodeid):
57 57 allowed = self.repo.ui.configlist("web", "allow_archive")
58 58 for i, spec in self.archive_specs.iteritems():
59 59 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
60 60 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
61 61
62 62 def listfilediffs(self, files, changeset):
63 63 for f in files[:self.maxfiles]:
64 64 yield self.t("filedifflink", node=hex(changeset), file=f)
65 65 if len(files) > self.maxfiles:
66 66 yield self.t("fileellipses")
67 67
68 68 def siblings(self, siblings=[], hiderev=None, **args):
69 69 siblings = [s for s in siblings if s.node() != nullid]
70 70 if len(siblings) == 1 and siblings[0].rev() == hiderev:
71 71 return
72 72 for s in siblings:
73 73 yield dict(node=hex(s.node()), rev=s.rev(), **args)
74 74
75 75 def renamelink(self, fl, node):
76 76 r = fl.renamed(node)
77 77 if r:
78 78 return [dict(file=r[0], node=hex(r[1]))]
79 79 return []
80 80
81 81 def showtag(self, t1, node=nullid, **args):
82 82 for t in self.repo.nodetags(node):
83 83 yield self.t(t1, tag=t, **args)
84 84
85 85 def diff(self, node1, node2, files):
86 86 def filterfiles(filters, files):
87 87 l = [x for x in files if x in filters]
88 88
89 89 for t in filters:
90 90 if t and t[-1] != os.sep:
91 91 t += os.sep
92 92 l += [x for x in files if x.startswith(t)]
93 93 return l
94 94
95 95 parity = [0]
96 96 def diffblock(diff, f, fn):
97 97 yield self.t("diffblock",
98 98 lines=prettyprintlines(diff),
99 99 parity=parity[0],
100 100 file=f,
101 101 filenode=hex(fn or nullid))
102 102 parity[0] = 1 - parity[0]
103 103
104 104 def prettyprintlines(diff):
105 105 for l in diff.splitlines(1):
106 106 if l.startswith('+'):
107 107 yield self.t("difflineplus", line=l)
108 108 elif l.startswith('-'):
109 109 yield self.t("difflineminus", line=l)
110 110 elif l.startswith('@'):
111 111 yield self.t("difflineat", line=l)
112 112 else:
113 113 yield self.t("diffline", line=l)
114 114
115 115 r = self.repo
116 116 cl = r.changelog
117 117 mf = r.manifest
118 118 change1 = cl.read(node1)
119 119 change2 = cl.read(node2)
120 120 mmap1 = mf.read(change1[0])
121 121 mmap2 = mf.read(change2[0])
122 122 date1 = util.datestr(change1[2])
123 123 date2 = util.datestr(change2[2])
124 124
125 125 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
126 126 if files:
127 127 modified, added, removed = map(lambda x: filterfiles(files, x),
128 128 (modified, added, removed))
129 129
130 130 diffopts = patch.diffopts(self.repo.ui)
131 131 for f in modified:
132 132 to = r.file(f).read(mmap1[f])
133 133 tn = r.file(f).read(mmap2[f])
134 134 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
135 135 opts=diffopts), f, tn)
136 136 for f in added:
137 137 to = None
138 138 tn = r.file(f).read(mmap2[f])
139 139 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
140 140 opts=diffopts), f, tn)
141 141 for f in removed:
142 142 to = r.file(f).read(mmap1[f])
143 143 tn = None
144 144 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
145 145 opts=diffopts), f, tn)
146 146
147 147 def changelog(self, ctx, shortlog=False):
148 148 pos = ctx.rev()
149 149 def changenav(**map):
150 150 def seq(factor, maxchanges=None):
151 151 if maxchanges:
152 152 yield maxchanges
153 153 if maxchanges >= 20 and maxchanges <= 40:
154 154 yield 50
155 155 else:
156 156 yield 1 * factor
157 157 yield 3 * factor
158 158 for f in seq(factor * 10):
159 159 yield f
160 160
161 161 l = []
162 162 last = 0
163 163 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
164 164 for f in seq(1, maxchanges):
165 165 if f < maxchanges or f <= last:
166 166 continue
167 167 if f > count:
168 168 break
169 169 last = f
170 170 r = "%d" % f
171 171 if pos + f < count:
172 172 l.append(("+" + r, pos + f))
173 173 if pos - f >= 0:
174 174 l.insert(0, ("-" + r, pos - f))
175 175
176 176 yield {"rev": 0, "label": "(0)"}
177 177
178 178 for label, rev in l:
179 179 yield {"label": label, "rev": rev}
180 180
181 181 yield {"label": "tip", "rev": "tip"}
182 182
183 183 def changelist(**map):
184 184 parity = (start - end) & 1
185 185 cl = self.repo.changelog
186 186 l = [] # build a list in forward order for efficiency
187 187 for i in range(start, end):
188 188 ctx = self.repo.changectx(i)
189 189 n = ctx.node()
190 190
191 191 l.insert(0, {"parity": parity,
192 192 "author": ctx.user(),
193 193 "parent": self.siblings(ctx.parents(), i - 1),
194 194 "child": self.siblings(ctx.children(), i + 1),
195 195 "changelogtag": self.showtag("changelogtag",n),
196 196 "desc": ctx.description(),
197 197 "date": ctx.date(),
198 198 "files": self.listfilediffs(ctx.files(), n),
199 199 "rev": i,
200 200 "node": hex(n)})
201 201 parity = 1 - parity
202 202
203 203 for e in l:
204 204 yield e
205 205
206 206 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
207 207 cl = self.repo.changelog
208 208 count = cl.count()
209 209 start = max(0, pos - maxchanges + 1)
210 210 end = min(count, start + maxchanges)
211 211 pos = end - 1
212 212
213 213 yield self.t(shortlog and 'shortlog' or 'changelog',
214 214 changenav=changenav,
215 215 node=hex(cl.tip()),
216 216 rev=pos, changesets=count, entries=changelist,
217 217 archives=self.archivelist("tip"))
218 218
219 219 def search(self, query):
220 220
221 221 def changelist(**map):
222 222 cl = self.repo.changelog
223 223 count = 0
224 224 qw = query.lower().split()
225 225
226 226 def revgen():
227 227 for i in range(cl.count() - 1, 0, -100):
228 228 l = []
229 229 for j in range(max(0, i - 100), i):
230 230 ctx = self.repo.changectx(j)
231 231 l.append(ctx)
232 232 l.reverse()
233 233 for e in l:
234 234 yield e
235 235
236 236 for ctx in revgen():
237 237 miss = 0
238 238 for q in qw:
239 239 if not (q in ctx.user().lower() or
240 240 q in ctx.description().lower() or
241 241 q in " ".join(ctx.files()[:20]).lower()):
242 242 miss = 1
243 243 break
244 244 if miss:
245 245 continue
246 246
247 247 count += 1
248 248 n = ctx.node()
249 249
250 250 yield self.t('searchentry',
251 251 parity=self.stripes(count),
252 252 author=ctx.user(),
253 253 parent=self.siblings(ctx.parents()),
254 254 child=self.siblings(ctx.children()),
255 255 changelogtag=self.showtag("changelogtag",n),
256 256 desc=ctx.description(),
257 257 date=ctx.date(),
258 258 files=self.listfilediffs(ctx.files(), n),
259 259 rev=ctx.rev(),
260 260 node=hex(n))
261 261
262 262 if count >= self.maxchanges:
263 263 break
264 264
265 265 cl = self.repo.changelog
266 266
267 267 yield self.t('search',
268 268 query=query,
269 269 node=hex(cl.tip()),
270 270 entries=changelist)
271 271
272 272 def changeset(self, ctx):
273 273 n = ctx.node()
274 274 parents = ctx.parents()
275 275 p1 = parents[0].node()
276 276
277 277 files = []
278 278 parity = 0
279 279 for f in ctx.files():
280 280 files.append(self.t("filenodelink",
281 281 node=hex(n), file=f,
282 282 parity=parity))
283 283 parity = 1 - parity
284 284
285 285 def diff(**map):
286 286 yield self.diff(p1, n, None)
287 287
288 288 yield self.t('changeset',
289 289 diff=diff,
290 290 rev=ctx.rev(),
291 291 node=hex(n),
292 292 parent=self.siblings(parents),
293 293 child=self.siblings(ctx.children()),
294 294 changesettag=self.showtag("changesettag",n),
295 295 author=ctx.user(),
296 296 desc=ctx.description(),
297 297 date=ctx.date(),
298 298 files=files,
299 299 archives=self.archivelist(hex(n)))
300 300
301 301 def filelog(self, fctx):
302 302 f = fctx.path()
303 303 fl = fctx.filelog()
304 304 count = fl.count()
305 305
306 306 def entries(**map):
307 307 l = []
308 308 parity = (count - 1) & 1
309 309
310 310 for i in range(count):
311 311 ctx = fctx.filectx(i)
312 312 n = fl.node(i)
313 313
314 314 l.insert(0, {"parity": parity,
315 315 "filerev": i,
316 316 "file": f,
317 317 "node": hex(ctx.node()),
318 318 "author": ctx.user(),
319 319 "date": ctx.date(),
320 320 "rename": self.renamelink(fl, n),
321 321 "parent": self.siblings(fctx.parents(), file=f),
322 322 "child": self.siblings(fctx.children(), file=f),
323 323 "desc": ctx.description()})
324 324 parity = 1 - parity
325 325
326 326 for e in l:
327 327 yield e
328 328
329 329 yield self.t("filelog", file=f, node=hex(fctx.node()), entries=entries)
330 330
331 331 def filerevision(self, fctx):
332 332 f = fctx.path()
333 333 text = fctx.data()
334 334 fl = fctx.filelog()
335 335 n = fctx.filenode()
336 336
337 337 mt = mimetypes.guess_type(f)[0]
338 338 rawtext = text
339 339 if util.binary(text):
340 340 mt = mt or 'application/octet-stream'
341 341 text = "(binary:%s)" % mt
342 342 mt = mt or 'text/plain'
343 343
344 344 def lines():
345 345 for l, t in enumerate(text.splitlines(1)):
346 346 yield {"line": t,
347 347 "linenumber": "% 6d" % (l + 1),
348 348 "parity": self.stripes(l)}
349 349
350 350 yield self.t("filerevision",
351 351 file=f,
352 352 path=_up(f),
353 353 text=lines(),
354 354 raw=rawtext,
355 355 mimetype=mt,
356 356 rev=fctx.rev(),
357 357 node=hex(fctx.node()),
358 358 author=fctx.user(),
359 359 date=fctx.date(),
360 360 parent=self.siblings(fctx.parents(), file=f),
361 361 child=self.siblings(fctx.children(), file=f),
362 362 rename=self.renamelink(fl, n),
363 363 permissions=fctx.manifest().execf(f))
364 364
365 365 def fileannotate(self, fctx):
366 366 f = fctx.path()
367 367 n = fctx.filenode()
368 368 fl = fctx.filelog()
369 369
370 370 def annotate(**map):
371 371 parity = 0
372 372 last = None
373 373 for f, l in fctx.annotate(follow=True):
374 374 fnode = f.filenode()
375 375 name = self.repo.ui.shortuser(f.user())
376 376
377 377 if last != fnode:
378 378 parity = 1 - parity
379 379 last = fnode
380 380
381 381 yield {"parity": parity,
382 382 "node": hex(f.node()),
383 383 "rev": f.rev(),
384 384 "author": name,
385 385 "file": f.path(),
386 386 "line": l}
387 387
388 388 yield self.t("fileannotate",
389 389 file=f,
390 390 annotate=annotate,
391 391 path=_up(f),
392 392 rev=fctx.rev(),
393 393 node=hex(fctx.node()),
394 394 author=fctx.user(),
395 395 date=fctx.date(),
396 396 rename=self.renamelink(fl, n),
397 397 parent=self.siblings(fctx.parents(), file=f),
398 398 child=self.siblings(fctx.children(), file=f),
399 399 permissions=fctx.manifest().execf(f))
400 400
401 401 def manifest(self, ctx, path):
402 402 mf = ctx.manifest()
403 403 node = ctx.node()
404 404
405 405 files = {}
406 406
407 407 p = path[1:]
408 408 if p and p[-1] != "/":
409 409 p += "/"
410 410 l = len(p)
411 411
412 412 for f,n in mf.items():
413 413 if f[:l] != p:
414 414 continue
415 415 remain = f[l:]
416 416 if "/" in remain:
417 417 short = remain[:remain.index("/") + 1] # bleah
418 418 files[short] = (f, None)
419 419 else:
420 420 short = os.path.basename(remain)
421 421 files[short] = (f, n)
422 422
423 423 def filelist(**map):
424 424 parity = 0
425 425 fl = files.keys()
426 426 fl.sort()
427 427 for f in fl:
428 428 full, fnode = files[f]
429 429 if not fnode:
430 430 continue
431 431
432 432 yield {"file": full,
433 433 "parity": self.stripes(parity),
434 434 "basename": f,
435 435 "size": ctx.filectx(full).size(),
436 436 "permissions": mf.execf(full)}
437 437 parity += 1
438 438
439 439 def dirlist(**map):
440 440 parity = 0
441 441 fl = files.keys()
442 442 fl.sort()
443 443 for f in fl:
444 444 full, fnode = files[f]
445 445 if fnode:
446 446 continue
447 447
448 448 yield {"parity": self.stripes(parity),
449 449 "path": os.path.join(path, f),
450 450 "basename": f[:-1]}
451 451 parity += 1
452 452
453 453 yield self.t("manifest",
454 454 rev=ctx.rev(),
455 455 node=hex(node),
456 456 path=path,
457 457 up=_up(path),
458 458 fentries=filelist,
459 459 dentries=dirlist,
460 460 archives=self.archivelist(hex(node)))
461 461
462 462 def tags(self):
463 463 cl = self.repo.changelog
464 464
465 465 i = self.repo.tagslist()
466 466 i.reverse()
467 467
468 468 def entries(notip=False, **map):
469 469 parity = 0
470 470 for k,n in i:
471 471 if notip and k == "tip": continue
472 472 yield {"parity": self.stripes(parity),
473 473 "tag": k,
474 474 "date": cl.read(n)[2],
475 475 "node": hex(n)}
476 476 parity += 1
477 477
478 478 yield self.t("tags",
479 479 node=hex(self.repo.changelog.tip()),
480 480 entries=lambda **x: entries(False, **x),
481 481 entriesnotip=lambda **x: entries(True, **x))
482 482
483 483 def summary(self):
484 484 cl = self.repo.changelog
485 485
486 486 i = self.repo.tagslist()
487 487 i.reverse()
488 488
489 489 def tagentries(**map):
490 490 parity = 0
491 491 count = 0
492 492 for k,n in i:
493 493 if k == "tip": # skip tip
494 494 continue;
495 495
496 496 count += 1
497 497 if count > 10: # limit to 10 tags
498 498 break;
499 499
500 500 c = cl.read(n)
501 501 t = c[2]
502 502
503 503 yield self.t("tagentry",
504 504 parity = self.stripes(parity),
505 505 tag = k,
506 506 node = hex(n),
507 507 date = t)
508 508 parity += 1
509 509
510 510 def changelist(**map):
511 511 parity = 0
512 512 cl = self.repo.changelog
513 513 l = [] # build a list in forward order for efficiency
514 514 for i in range(start, end):
515 515 n = cl.node(i)
516 516 changes = cl.read(n)
517 517 hn = hex(n)
518 518 t = changes[2]
519 519
520 520 l.insert(0, self.t(
521 521 'shortlogentry',
522 522 parity = parity,
523 523 author = changes[1],
524 524 desc = changes[4],
525 525 date = t,
526 526 rev = i,
527 527 node = hn))
528 528 parity = 1 - parity
529 529
530 530 yield l
531 531
532 532 count = cl.count()
533 533 start = max(0, count - self.maxchanges)
534 534 end = min(count, start + self.maxchanges)
535 535
536 536 yield self.t("summary",
537 537 desc = self.repo.ui.config("web", "description", "unknown"),
538 538 owner = (self.repo.ui.config("ui", "username") or # preferred
539 539 self.repo.ui.config("web", "contact") or # deprecated
540 540 self.repo.ui.config("web", "author", "unknown")), # also
541 541 lastchange = cl.read(cl.tip())[2],
542 542 tags = tagentries,
543 543 shortlog = changelist,
544 544 node = hex(cl.tip()),
545 545 archives=self.archivelist("tip"))
546 546
547 547 def filediff(self, fctx):
548 548 n = fctx.node()
549 549 path = fctx.path()
550 550 parents = fctx.changectx().parents()
551 551 p1 = parents[0].node()
552 552
553 553 def diff(**map):
554 554 yield self.diff(p1, n, [path])
555 555
556 556 yield self.t("filediff",
557 557 file=path,
558 558 node=hex(n),
559 559 rev=fctx.rev(),
560 560 parent=self.siblings(parents),
561 561 child=self.siblings(fctx.children()),
562 562 diff=diff)
563 563
564 564 archive_specs = {
565 565 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
566 566 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
567 567 'zip': ('application/zip', 'zip', '.zip', None),
568 568 }
569 569
570 570 def archive(self, req, cnode, type_):
571 571 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
572 572 name = "%s-%s" % (reponame, short(cnode))
573 573 mimetype, artype, extension, encoding = self.archive_specs[type_]
574 574 headers = [('Content-type', mimetype),
575 575 ('Content-disposition', 'attachment; filename=%s%s' %
576 576 (name, extension))]
577 577 if encoding:
578 578 headers.append(('Content-encoding', encoding))
579 579 req.header(headers)
580 580 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
581 581
582 582 # add tags to things
583 583 # tags -> list of changesets corresponding to tags
584 584 # find tag, changeset, file
585 585
586 586 def cleanpath(self, path):
587 587 p = util.normpath(path)
588 588 if p[:2] == "..":
589 589 raise Exception("suspicious path")
590 590 return p
591 591
592 592 def run(self):
593 593 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
594 594 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
595 595 import mercurial.hgweb.wsgicgi as wsgicgi
596 596 from request import wsgiapplication
597 597 def make_web_app():
598 598 return self
599 599 wsgicgi.launch(wsgiapplication(make_web_app))
600 600
601 601 def run_wsgi(self, req):
602 602 def header(**map):
603 603 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
604 604 msg = mimetools.Message(header_file, 0)
605 605 req.header(msg.items())
606 606 yield header_file.read()
607 607
608 608 def rawfileheader(**map):
609 609 req.header([('Content-type', map['mimetype']),
610 610 ('Content-disposition', 'filename=%s' % map['file']),
611 611 ('Content-length', str(len(map['raw'])))])
612 612 yield ''
613 613
614 614 def footer(**map):
615 615 yield self.t("footer",
616 616 motd=self.repo.ui.config("web", "motd", ""),
617 617 **map)
618 618
619 619 def expand_form(form):
620 620 shortcuts = {
621 621 'cl': [('cmd', ['changelog']), ('rev', None)],
622 622 'sl': [('cmd', ['shortlog']), ('rev', None)],
623 623 'cs': [('cmd', ['changeset']), ('node', None)],
624 624 'f': [('cmd', ['file']), ('filenode', None)],
625 625 'fl': [('cmd', ['filelog']), ('filenode', None)],
626 626 'fd': [('cmd', ['filediff']), ('node', None)],
627 627 'fa': [('cmd', ['annotate']), ('filenode', None)],
628 628 'mf': [('cmd', ['manifest']), ('manifest', None)],
629 629 'ca': [('cmd', ['archive']), ('node', None)],
630 630 'tags': [('cmd', ['tags'])],
631 631 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
632 632 'static': [('cmd', ['static']), ('file', None)]
633 633 }
634 634
635 635 for k in shortcuts.iterkeys():
636 636 if form.has_key(k):
637 637 for name, value in shortcuts[k]:
638 638 if value is None:
639 639 value = form[k]
640 640 form[name] = value
641 641 del form[k]
642 642
643 643 def rewrite_request(req):
644 644 '''translate new web interface to traditional format'''
645 645
646 646 def spliturl(req):
647 647 def firstitem(query):
648 648 return query.split('&', 1)[0].split(';', 1)[0]
649 649
650 650 def normurl(url):
651 651 inner = '/'.join([x for x in url.split('/') if x])
652 652 tl = len(url) > 1 and url.endswith('/') and '/' or ''
653 653
654 654 return '%s%s%s' % (url.startswith('/') and '/' or '',
655 655 inner, tl)
656 656
657 657 root = normurl(req.env.get('REQUEST_URI', '').split('?', 1)[0])
658 658 pi = normurl(req.env.get('PATH_INFO', ''))
659 659 if pi:
660 660 # strip leading /
661 661 pi = pi[1:]
662 662 if pi:
663 663 root = root[:-len(pi)]
664 664 if req.env.has_key('REPO_NAME'):
665 665 rn = req.env['REPO_NAME'] + '/'
666 666 root += rn
667 667 query = pi[len(rn):]
668 668 else:
669 669 query = pi
670 670 else:
671 671 root += '?'
672 672 query = firstitem(req.env['QUERY_STRING'])
673 673
674 674 return (root, query)
675 675
676 676 req.url, query = spliturl(req)
677 677
678 678 if req.form.has_key('cmd'):
679 679 # old style
680 680 return
681 681
682 682 args = query.split('/', 2)
683 683 if not args or not args[0]:
684 684 return
685 685
686 686 cmd = args.pop(0)
687 687 style = cmd.rfind('-')
688 688 if style != -1:
689 689 req.form['style'] = [cmd[:style]]
690 690 cmd = cmd[style+1:]
691 691 # avoid accepting e.g. style parameter as command
692 692 if hasattr(self, 'do_' + cmd):
693 693 req.form['cmd'] = [cmd]
694 694
695 695 if args and args[0]:
696 696 node = args.pop(0)
697 697 req.form['node'] = [node]
698 698 if args:
699 699 req.form['file'] = args
700 700
701 701 if cmd == 'static':
702 702 req.form['file'] = req.form['node']
703 703 elif cmd == 'archive':
704 704 fn = req.form['node'][0]
705 705 for type_, spec in self.archive_specs.iteritems():
706 706 ext = spec[2]
707 707 if fn.endswith(ext):
708 708 req.form['node'] = [fn[:-len(ext)]]
709 709 req.form['type'] = [type_]
710 710
711 def sessionvars(**map):
712 fields = []
713 if req.form.has_key('style'):
714 style = req.form['style'][0]
715 if style != self.repo.ui.config('web', 'style', ''):
716 fields.append(('style', style))
717
718 for name, value in fields:
719 yield dict(name=name, value=value)
720
711 721 def queryprefix(**map):
712 722 return req.url[-1] == '?' and ';' or '?'
713 723
714 724 def getentries(**map):
715 725 fields = {}
716 726 if req.form.has_key('style'):
717 727 style = req.form['style'][0]
718 728 if style != self.repo.ui.config('web', 'style', ''):
719 729 fields['style'] = style
720 730
721 731 if fields:
722 732 fields = ['%s=%s' % (k, urllib.quote(v))
723 733 for k, v in fields.iteritems()]
724 734 yield '%s%s' % (queryprefix(), ';'.join(fields))
725 735 else:
726 736 yield ''
727 737
728 738 self.refresh()
729 739
730 740 expand_form(req.form)
731 741 rewrite_request(req)
732 742
733 743 style = self.repo.ui.config("web", "style", "")
734 744 if req.form.has_key('style'):
735 745 style = req.form['style'][0]
736 746 mapfile = style_map(self.templatepath, style)
737 747
738 748 if not req.url:
739 749 port = req.env["SERVER_PORT"]
740 750 port = port != "80" and (":" + port) or ""
741 751 uri = req.env["REQUEST_URI"]
742 752 if "?" in uri:
743 753 uri = uri.split("?")[0]
744 754 req.url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
745 755
746 756 if not self.reponame:
747 757 self.reponame = (self.repo.ui.config("web", "name")
748 758 or req.env.get('REPO_NAME')
749 759 or req.url.strip('/') or self.repo.root)
750 760
751 761 self.t = templater.templater(mapfile, templater.common_filters,
752 762 defaults={"url": req.url,
753 763 "repo": self.reponame,
754 764 "header": header,
755 765 "footer": footer,
756 766 "rawfileheader": rawfileheader,
767 "sessionvars": sessionvars,
757 768 "queryprefix": queryprefix,
758 769 "getentries": getentries
759 770 })
760 771
761 772 if not req.form.has_key('cmd'):
762 773 req.form['cmd'] = [self.t.cache['default'],]
763 774
764 775 cmd = req.form['cmd'][0]
765 776
766 777 method = getattr(self, 'do_' + cmd, None)
767 778 if method:
768 779 try:
769 780 method(req)
770 781 except (hg.RepoError, revlog.RevlogError), inst:
771 782 req.write(self.t("error", error=str(inst)))
772 783 else:
773 784 req.write(self.t("error", error='No such method: ' + cmd))
774 785
775 786 def changectx(self, req):
776 787 if req.form.has_key('node'):
777 788 changeid = req.form['node'][0]
778 789 elif req.form.has_key('manifest'):
779 790 changeid = req.form['manifest'][0]
780 791 else:
781 792 changeid = self.repo.changelog.count() - 1
782 793
783 794 try:
784 795 ctx = self.repo.changectx(changeid)
785 796 except hg.RepoError:
786 797 man = self.repo.manifest
787 798 mn = man.lookup(changeid)
788 799 ctx = self.repo.changectx(man.linkrev(mn))
789 800
790 801 return ctx
791 802
792 803 def filectx(self, req):
793 804 path = self.cleanpath(req.form['file'][0])
794 805 if req.form.has_key('node'):
795 806 changeid = req.form['node'][0]
796 807 else:
797 808 changeid = req.form['filenode'][0]
798 809 try:
799 810 ctx = self.repo.changectx(changeid)
800 811 fctx = ctx.filectx(path)
801 812 except hg.RepoError:
802 813 fctx = self.repo.filectx(path, fileid=changeid)
803 814
804 815 return fctx
805 816
806 817 def stripes(self, parity):
807 818 "make horizontal stripes for easier reading"
808 819 if self.stripecount:
809 820 return (1 + parity / self.stripecount) & 1
810 821 else:
811 822 return 0
812 823
813 824 def do_log(self, req):
814 825 if req.form.has_key('file') and req.form['file'][0]:
815 826 self.do_filelog(req)
816 827 else:
817 828 self.do_changelog(req)
818 829
819 830 def do_rev(self, req):
820 831 self.do_changeset(req)
821 832
822 833 def do_file(self, req):
823 834 path = req.form.get('file', [''])[0]
824 835 if path:
825 836 try:
826 837 req.write(self.filerevision(self.filectx(req)))
827 838 return
828 839 except hg.RepoError:
829 840 pass
830 841 path = self.cleanpath(path)
831 842
832 843 req.write(self.manifest(self.changectx(req), '/' + path))
833 844
834 845 def do_diff(self, req):
835 846 self.do_filediff(req)
836 847
837 848 def do_changelog(self, req, shortlog = False):
838 849 if req.form.has_key('node'):
839 850 ctx = self.changectx(req)
840 851 else:
841 852 if req.form.has_key('rev'):
842 853 hi = req.form['rev'][0]
843 854 else:
844 855 hi = self.repo.changelog.count() - 1
845 856 try:
846 857 ctx = self.repo.changectx(hi)
847 858 except hg.RepoError:
848 859 req.write(self.search(hi)) # XXX redirect to 404 page?
849 860 return
850 861
851 862 req.write(self.changelog(ctx, shortlog = shortlog))
852 863
853 864 def do_shortlog(self, req):
854 865 self.do_changelog(req, shortlog = True)
855 866
856 867 def do_changeset(self, req):
857 868 req.write(self.changeset(self.changectx(req)))
858 869
859 870 def do_manifest(self, req):
860 871 req.write(self.manifest(self.changectx(req),
861 872 self.cleanpath(req.form['path'][0])))
862 873
863 874 def do_tags(self, req):
864 875 req.write(self.tags())
865 876
866 877 def do_summary(self, req):
867 878 req.write(self.summary())
868 879
869 880 def do_filediff(self, req):
870 881 req.write(self.filediff(self.filectx(req)))
871 882
872 883 def do_annotate(self, req):
873 884 req.write(self.fileannotate(self.filectx(req)))
874 885
875 886 def do_filelog(self, req):
876 887 req.write(self.filelog(self.filectx(req)))
877 888
878 889 def do_heads(self, req):
879 890 resp = " ".join(map(hex, self.repo.heads())) + "\n"
880 891 req.httphdr("application/mercurial-0.1", length=len(resp))
881 892 req.write(resp)
882 893
883 894 def do_branches(self, req):
884 895 nodes = []
885 896 if req.form.has_key('nodes'):
886 897 nodes = map(bin, req.form['nodes'][0].split(" "))
887 898 resp = cStringIO.StringIO()
888 899 for b in self.repo.branches(nodes):
889 900 resp.write(" ".join(map(hex, b)) + "\n")
890 901 resp = resp.getvalue()
891 902 req.httphdr("application/mercurial-0.1", length=len(resp))
892 903 req.write(resp)
893 904
894 905 def do_between(self, req):
895 906 if req.form.has_key('pairs'):
896 907 pairs = [map(bin, p.split("-"))
897 908 for p in req.form['pairs'][0].split(" ")]
898 909 resp = cStringIO.StringIO()
899 910 for b in self.repo.between(pairs):
900 911 resp.write(" ".join(map(hex, b)) + "\n")
901 912 resp = resp.getvalue()
902 913 req.httphdr("application/mercurial-0.1", length=len(resp))
903 914 req.write(resp)
904 915
905 916 def do_changegroup(self, req):
906 917 req.httphdr("application/mercurial-0.1")
907 918 nodes = []
908 919 if not self.allowpull:
909 920 return
910 921
911 922 if req.form.has_key('roots'):
912 923 nodes = map(bin, req.form['roots'][0].split(" "))
913 924
914 925 z = zlib.compressobj()
915 926 f = self.repo.changegroup(nodes, 'serve')
916 927 while 1:
917 928 chunk = f.read(4096)
918 929 if not chunk:
919 930 break
920 931 req.write(z.compress(chunk))
921 932
922 933 req.write(z.flush())
923 934
924 935 def do_archive(self, req):
925 936 changeset = self.repo.lookup(req.form['node'][0])
926 937 type_ = req.form['type'][0]
927 938 allowed = self.repo.ui.configlist("web", "allow_archive")
928 939 if (type_ in self.archives and (type_ in allowed or
929 940 self.repo.ui.configbool("web", "allow" + type_, False))):
930 941 self.archive(req, changeset, type_)
931 942 return
932 943
933 944 req.write(self.t("error"))
934 945
935 946 def do_static(self, req):
936 947 fname = req.form['file'][0]
937 948 static = self.repo.ui.config("web", "static",
938 949 os.path.join(self.templatepath,
939 950 "static"))
940 951 req.write(staticfile(static, fname, req)
941 952 or self.t("error", error="%r not found" % fname))
942 953
943 954 def do_capabilities(self, req):
944 955 caps = ['unbundle']
945 956 if self.repo.ui.configbool('server', 'uncompressed'):
946 957 caps.append('stream=%d' % self.repo.revlogversion)
947 958 resp = ' '.join(caps)
948 959 req.httphdr("application/mercurial-0.1", length=len(resp))
949 960 req.write(resp)
950 961
951 962 def check_perm(self, req, op, default):
952 963 '''check permission for operation based on user auth.
953 964 return true if op allowed, else false.
954 965 default is policy to use if no config given.'''
955 966
956 967 user = req.env.get('REMOTE_USER')
957 968
958 969 deny = self.repo.ui.configlist('web', 'deny_' + op)
959 970 if deny and (not user or deny == ['*'] or user in deny):
960 971 return False
961 972
962 973 allow = self.repo.ui.configlist('web', 'allow_' + op)
963 974 return (allow and (allow == ['*'] or user in allow)) or default
964 975
965 976 def do_unbundle(self, req):
966 977 def bail(response, headers={}):
967 978 length = int(req.env['CONTENT_LENGTH'])
968 979 for s in util.filechunkiter(req, limit=length):
969 980 # drain incoming bundle, else client will not see
970 981 # response when run outside cgi script
971 982 pass
972 983 req.httphdr("application/mercurial-0.1", headers=headers)
973 984 req.write('0\n')
974 985 req.write(response)
975 986
976 987 # require ssl by default, auth info cannot be sniffed and
977 988 # replayed
978 989 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
979 990 if ssl_req:
980 991 if not req.env.get('HTTPS'):
981 992 bail(_('ssl required\n'))
982 993 return
983 994 proto = 'https'
984 995 else:
985 996 proto = 'http'
986 997
987 998 # do not allow push unless explicitly allowed
988 999 if not self.check_perm(req, 'push', False):
989 1000 bail(_('push not authorized\n'),
990 1001 headers={'status': '401 Unauthorized'})
991 1002 return
992 1003
993 1004 req.httphdr("application/mercurial-0.1")
994 1005
995 1006 their_heads = req.form['heads'][0].split(' ')
996 1007
997 1008 def check_heads():
998 1009 heads = map(hex, self.repo.heads())
999 1010 return their_heads == [hex('force')] or their_heads == heads
1000 1011
1001 1012 # fail early if possible
1002 1013 if not check_heads():
1003 1014 bail(_('unsynced changes\n'))
1004 1015 return
1005 1016
1006 1017 # do not lock repo until all changegroup data is
1007 1018 # streamed. save to temporary file.
1008 1019
1009 1020 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1010 1021 fp = os.fdopen(fd, 'wb+')
1011 1022 try:
1012 1023 length = int(req.env['CONTENT_LENGTH'])
1013 1024 for s in util.filechunkiter(req, limit=length):
1014 1025 fp.write(s)
1015 1026
1016 1027 lock = self.repo.lock()
1017 1028 try:
1018 1029 if not check_heads():
1019 1030 req.write('0\n')
1020 1031 req.write(_('unsynced changes\n'))
1021 1032 return
1022 1033
1023 1034 fp.seek(0)
1024 1035
1025 1036 # send addchangegroup output to client
1026 1037
1027 1038 old_stdout = sys.stdout
1028 1039 sys.stdout = cStringIO.StringIO()
1029 1040
1030 1041 try:
1031 1042 url = 'remote:%s:%s' % (proto,
1032 1043 req.env.get('REMOTE_HOST', ''))
1033 1044 ret = self.repo.addchangegroup(fp, 'serve', url)
1034 1045 finally:
1035 1046 val = sys.stdout.getvalue()
1036 1047 sys.stdout = old_stdout
1037 1048 req.write('%d\n' % ret)
1038 1049 req.write(val)
1039 1050 finally:
1040 1051 lock.release()
1041 1052 finally:
1042 1053 fp.close()
1043 1054 os.unlink(tempname)
1044 1055
1045 1056 def do_stream_out(self, req):
1046 1057 req.httphdr("application/mercurial-0.1")
1047 1058 streamclone.stream_out(self.repo, req)
@@ -1,36 +1,38 b''
1 1 #header#
2 2 <title>#repo|escape#: changelog</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="#url#rss-log" title="RSS feed for #repo|escape#">
5 5 </head>
6 6 <body>
7 7
8 8 <div class="buttons">
9 9 <a href="#url#shortlog/#rev#{getentries}">shortlog</a>
10 10 <a href="#url#tags{getentries}">tags</a>
11 11 <a href="#url#file/#node|short#{getentries}">manifest</a>
12 12 #archives%archiveentry#
13 13 <a type="application/rss+xml" href="#url#rss-log">rss</a>
14 14 </div>
15 15
16 16 <h2>changelog for #repo|escape#</h2>
17 17
18 18 <form action="#url#log">
19 {sessionvars%hiddenformentry}
19 20 <p>
20 21 <label for="search1">search:</label>
21 22 <input name="rev" id="search1" type="text" size="30">
22 23 navigate: <small class="navigate">#changenav%naventry#</small>
23 24 </p>
24 25 </form>
25 26
26 27 #entries%changelogentry#
27 28
28 29 <form action="#url#log">
30 {sessionvars%hiddenformentry}
29 31 <p>
30 32 <label for="search2">search:</label>
31 33 <input name="rev" id="search2" type="text" size="30">
32 34 navigate: <small class="navigate">#changenav%naventry#</small>
33 35 </p>
34 36 </form>
35 37
36 38 #footer#
@@ -1,32 +1,32 b''
1 1 #header#
2 2 <title>#repo|escape#: Changelog</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="{url}rss-log" 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="{url}summary{getentries}">#repo|escape#</a> / changelog
10 10 </div>
11 11
12 12 <form action="{url}log">
13 {sessionvars%hiddenformentry}
13 14 <div class="search">
14 <input type="hidden" name="style" value="gitweb" />
15 15 <input type="text" name="rev" />
16 16 </div>
17 17 </form>
18 18 </div>
19 19
20 20 <div class="page_nav">
21 21 <a href="{url}summary{getentries}">summary</a> | <a href="{url}shortlog/#rev#{getentries}">shortlog</a> | changelog | <a href="{url}tags{getentries}">tags</a> | <a href="{url}file/#node|short#{getentries}">manifest</a>#archives%archiveentry#<br/>
22 22 <br/>
23 23 #changenav%naventry#<br/>
24 24 </div>
25 25
26 26 #entries%changelogentry#
27 27
28 28 <div class="page_nav">
29 29 #changenav%naventry#<br/>
30 30 </div>
31 31
32 32 #footer#
@@ -1,53 +1,54 b''
1 1 default = 'summary'
2 2 header = header.tmpl
3 3 footer = footer.tmpl
4 4 search = search.tmpl
5 5 changelog = changelog.tmpl
6 6 summary = summary.tmpl
7 7 error = error.tmpl
8 8 naventry = '<a href="#url#log/#rev#{getentries}">#label|escape#</a> '
9 9 navshortentry = '<a href="#url#shortlog/#rev#{getentries}">#label|escape#</a> '
10 10 filedifflink = '<a href="#url#diff/#node|short#/#file|urlescape#{getentries}">#file|escape#</a> '
11 11 filenodelink = '<tr class="parity#parity#"><td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{getentries}">#file|escape#</a></td><td></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{getentries}">file</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{getentries}">annotate</a> | <a href="#url#diff/#node|short#/#file|urlescape#{getentries}">diff</a> | <a href="#url#log/#node|short#/#file|urlescape#{getentries}">revisions</a></td></tr>'
12 12 fileellipses = '...'
13 13 changelogentry = changelogentry.tmpl
14 14 searchentry = changelogentry.tmpl
15 15 changeset = changeset.tmpl
16 16 manifest = manifest.tmpl
17 17 manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{getentries}">#basename|escape#/</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{getentries}">manifest</a></td></tr>'
18 18 manifestfileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td style="font-family:monospace" align=right>#size#</td><td class="list"><a class="list" href="#url#file/#node|short#/#file|urlescape#{getentries}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{getentries}">file</a> | <a href="#url#log/#node|short#/#file|urlescape#{getentries}">revisions</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{getentries}">annotate</a></td></tr>'
19 19 filerevision = filerevision.tmpl
20 20 fileannotate = fileannotate.tmpl
21 21 filediff = filediff.tmpl
22 22 filelog = filelog.tmpl
23 23 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr"> #linenumber#</span> #line|escape#</pre></div>'
24 24 annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="#url#annotate/#node|short#/#file|urlescape#{getentries}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
25 25 difflineplus = '<div style="color:#008800;">#line|escape#</div>'
26 26 difflineminus = '<div style="color:#cc0000;">#line|escape#</div>'
27 27 difflineat = '<div style="color:#990099;">#line|escape#</div>'
28 28 diffline = '<div>#line|escape#</div>'
29 29 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
30 30 changesetparent = '<tr><td>parent</td><td style="font-family:monospace"><a class="list" href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
31 31 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="{url}file/{node|short}/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
32 32 filerename = '<tr><td class="metatag">parent:</td><td><a href="{url}file/#node|short#/#file|urlescape#{getentries}">#file|escape#@#node|short#</a></td></tr>'
33 33 filelogrename = '| <a href="{url}file/#node|short#/#file|urlescape#{getentries}">base</a>'
34 34 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="{url}annotate/{node|short}/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
35 35 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="{url}rev/#node|short#{getentries}">#node|short#</a></td></tr>'
36 36 changesetchild = '<tr><td>child</td><td style="font-family:monospace"><a class="list" href="{url}rev/#node|short#{getentries}">#node|short#</a></td></tr>'
37 37 filerevchild = '<tr><td class="metatag">child:</td><td><a href="{url}file/{node|short}#file|urlescape#{getentries}">#node|short#</a></td></tr>'
38 38 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="{url}annotate/{node|short}/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
39 39 tags = tags.tmpl
40 40 tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/{node|short}{getentries}"><b>#tag|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{getentries}">changeset</a> | <a href="{url}log/#node|short#{getentries}">changelog</a> | <a href="{url}file/#node|short#{getentries}">manifest</a></td></tr>'
41 41 diffblock = '<pre>#lines#</pre>'
42 42 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
43 43 changesettag = '<tr><td>tag</td><td>#tag|escape#</td></tr>'
44 44 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="{url}rev/#node|short#{getentries}">#node|short#</a></td></tr>'
45 45 filelogparent = '<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="{url}file/{node|short}/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
46 46 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="{url}rev/#node|short#{getentries}">#node|short#</a></td></tr>'
47 47 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="{url}file{node|short}/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
48 48 shortlog = shortlog.tmpl
49 49 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="{url}rev/#node|short#{getentries}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{getentries}">changeset</a> | <a href="{url}file/#node|short#{getentries}">manifest</a></td></tr>'
50 50 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/#node|short#{getentries}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}file/#node|short#/#file|urlescape#{getentries}">file</a> | <!-- FIXME: <a href="{url}diff/#node|short#/#file|urlescape#{getentries}">diff</a> | --> <a href="{url}annotate/#node|short#/#file|urlescape#{getentries}">annotate</a> #rename%filelogrename#</td></tr>'
51 51 archiveentry = ' | <a href="{url}archive/{node|short}{extension}">#type|escape#</a> '
52 52 indexentry = '<tr class="parity#parity#"><td><a class="list" href="#url#"><b>#name|escape#</b></a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a class="rss_logo" href="#url#rss-log">RSS</a> #archives%archiveentry#</td></tr>'
53 53 index = index.tmpl
54 hiddenformentry = '<input type="hidden" name="#name#" value="#value|escape#" />'
@@ -1,26 +1,27 b''
1 1 #header#
2 2 <div class="page_nav">
3 3 <a href="{url}summary{getentries}">summary</a> |
4 4 <a href="{url}shortlog{getentries}">shortlog</a> |
5 5 <a href="{url}log{getentries}">changelog</a> |
6 6 <a href="{url}tags{getentries}">tags</a> |
7 7 <a href="{url}file/#node|short#{getentries}">manifest</a><br/>
8 8 </div>
9 9
10 10 <h2>searching for #query|escape#</h2>
11 11
12 12 <form action="{url}log">
13 {sessionvars%hiddenformentry}
13 14 search:
14 <input type="hidden" name="style" value="gitweb">
15 15 <input name="rev" type="text" width="30" value="#query|escape#">
16 16 </form>
17 17
18 18 #entries#
19 19
20 20 <form action="{url}log">
21 {sessionvars%hiddenformentry}
21 22 search:
22 23 <input type="hidden" name="style" value="gitweb">
23 24 <input name="rev" type="text" width="30">
24 25 </form>
25 26
26 27 #footer#
@@ -1,34 +1,34 b''
1 1 #header#
2 2 <title>#repo|escape#: Shortlog</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="{url}rss-log" 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="{url}summary{getentries}">#repo|escape#</a> / shortlog
10 10 </div>
11 11
12 12 <form action="{url}log">
13 {sessionvars%hiddenformentry}
13 14 <div class="search">
14 <input type="hidden" name="style" value="gitweb" />
15 15 <input type="text" name="rev" />
16 16 </div>
17 17 </form>
18 18 </div>
19 19 <div class="page_nav">
20 20 <a href="{url}summary{getentries}">summary</a> |
21 21 shortlog |
22 22 <a href="{url}log/#rev#{getentries}">changelog</a> |
23 23 <a href="{url}tags{getentries}">tags</a> |
24 24 <a href="{url}file/#node|short#{getentries}">manifest</a>#archives%archiveentry#<br/>
25 25 <br/>
26 26
27 27 #changenav%navshortentry#<br/>
28 28 </div>
29 29
30 30 <table cellspacing="0">
31 31 #entries%shortlogentry#
32 32 </table>
33 33
34 34 #footer#
@@ -1,53 +1,54 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 shortlog = shortlog.tmpl
7 7 shortlogentry = shortlogentry.tmpl
8 8 naventry = '<a href="#url#log/#rev#{getentries}">#label|escape#</a> '
9 9 navshortentry = '<a href="#url#shortlog/#rev#{getentries}">#label|escape#</a> '
10 10 filedifflink = '<a href="#url#diff/#node|short#/#file|urlescape#{getentries}">#file|escape#</a> '
11 11 filenodelink = '<a href="#url#file/#node|short#/#file|urlescape#{getentries}">#file|escape#</a> '
12 12 fileellipses = '...'
13 13 changelogentry = changelogentry.tmpl
14 14 searchentry = changelogentry.tmpl
15 15 changeset = changeset.tmpl
16 16 manifest = manifest.tmpl
17 17 manifestdirentry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td><a href="#url#file/#node|short##path|urlescape#{getentries}">#basename|escape#/</a>'
18 18 manifestfileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td align=right><tt>#size#</tt>&nbsp;<td><a href="#url#file/#node|short#/#file|urlescape#{getentries}">#basename|escape#</a>'
19 19 filerevision = filerevision.tmpl
20 20 fileannotate = fileannotate.tmpl
21 21 filediff = filediff.tmpl
22 22 filelog = filelog.tmpl
23 23 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
24 24 filelogentry = filelogentry.tmpl
25 25 annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="#url#annotate/#node|short#/#file|urlescape#{getentries}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
26 26 difflineplus = '<span class="plusline">#line|escape#</span>'
27 27 difflineminus = '<span class="minusline">#line|escape#</span>'
28 28 difflineat = '<span class="atline">#line|escape#</span>'
29 29 diffline = '#line|escape#'
30 30 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
31 31 changesetparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
32 32 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="#url#file/#node|short#/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
33 33 filerename = '<tr><td class="metatag">parent:</td><td><a href="#url#file/#node|short#/#file|urlescape#{getentries}">#file|escape#@#node|short#</a></td></tr>'
34 34 filelogrename = '<tr><th>base:</th><td><a href="#url#file/#node|short#/#file|urlescape#{getentries}">#file|escape#@#node|short#</a></td></tr>'
35 35 fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="#url#annotate/#node|short#/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
36 36 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
37 37 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
38 38 filerevchild = '<tr><td class="metatag">child:</td><td><a href="#url#file/#node|short#/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
39 39 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="#url#annotate/#node|short#/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
40 40 tags = tags.tmpl
41 41 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="#url#rev/#node|short#{getentries}">#tag|escape#</a></li>'
42 42 diffblock = '<pre class="parity#parity#">#lines#</pre>'
43 43 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
44 44 changesettag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
45 45 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
46 46 filelogparent = '<tr><th>parent #rev#:</th><td><a href="#url#file/#node|short#/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
47 47 filediffchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="#url#rev/#node|short#{getentries}">#node|short#</a></td></tr>'
48 48 filelogchild = '<tr><th>child #rev#:</th><td><a href="#url#file/#node|short#/#file|urlescape#{getentries}">#node|short#</a></td></tr>'
49 49 indexentry = '<tr class="parity#parity#"><td><a href="#url#">#name|escape#</a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a href="#url#rss-log">RSS</a> #archives%archiveentry#</td></tr>'
50 50 index = index.tmpl
51 51 archiveentry = '<a href="#url#archive/#node|short##extension|urlescape#">#type|escape#</a> '
52 52 notfound = notfound.tmpl
53 53 error = error.tmpl
54 hiddenformentry = '<input type="hidden" name="#name#" value="#value|escape#" />'
@@ -1,31 +1,33 b''
1 1 #header#
2 2 <title>#repo|escape#: searching for #query|escape#</title>
3 3 </head>
4 4 <body>
5 5
6 6 <div class="buttons">
7 7 <a href="#url#log{getentries}">changelog</a>
8 8 <a href="#url#shortlog{getentries}">shortlog</a>
9 9 <a href="#url#tags{getentries}">tags</a>
10 10 <a href="#url#file/#node|short#{getentries}">manifest</a>
11 11 </div>
12 12
13 13 <h2>searching for #query|escape#</h2>
14 14
15 15 <form>
16 {sessionvars%hiddenformentry}
16 17 <p>
17 18 search:
18 19 <input name="rev" type="text" width="30" value="#query|escape#">
19 20 </p>
20 21 </form>
21 22
22 23 #entries#
23 24
24 25 <form>
26 {sessionvars%hiddenformentry}
25 27 <p>
26 28 search:
27 29 <input name="rev" type="text" width="30" value="#query|escape#">
28 30 </p>
29 31 </form>
30 32
31 33 #footer#
@@ -1,36 +1,38 b''
1 1 #header#
2 2 <title>#repo|escape#: shortlog</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="#url#rss-log" title="RSS feed for #repo|escape#">
5 5 </head>
6 6 <body>
7 7
8 8 <div class="buttons">
9 9 <a href="#url#log/#rev#{getentries}">changelog</a>
10 10 <a href="#url#tags{getentries}">tags</a>
11 11 <a href="#url#file/#node|short#/{getentries}">manifest</a>
12 12 #archives%archiveentry#
13 13 <a type="application/rss+xml" href="#url#rss-log">rss</a>
14 14 </div>
15 15
16 16 <h2>shortlog for #repo|escape#</h2>
17 17
18 18 <form action="#url#log">
19 {sessionvars%hiddenformentry}
19 20 <p>
20 21 <label for="search1">search:</label>
21 22 <input name="rev" id="search1" type="text" size="30">
22 23 navigate: <small class="navigate">#changenav%navshortentry#</small>
23 24 </p>
24 25 </form>
25 26
26 27 #entries%shortlogentry#
27 28
28 29 <form action="#url#log">
30 {sessionvars%hiddenformentry}
29 31 <p>
30 32 <label for="search2">search:</label>
31 33 <input name="rev" id="search2" type="text" size="30">
32 34 navigate: <small class="navigate">#changenav%navshortentry#</small>
33 35 </p>
34 36 </form>
35 37
36 38 #footer#
General Comments 0
You need to be logged in to leave comments. Login now