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