Mercurial > hg > mercurial-crew-with-dirclash
annotate mercurial/hgweb.py @ 535:fba26990604a
Deal with failed clone/transaction interaction
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Deal with failed clone/transaction interaction
> What is happening is that something in the transaction machinery is
> causing the directory to be completely recreated.
The transaction gets rolled back by its destructor. This is critical
so it happens whenever an exception occurs that unwinds the stack.
Unfortunately, what's happening with clone is we're trying to delete
the directory during exception propagation. And a reference to the
transaction is held in the exception backtrace stack frames so it
still exists until the exception is completely resolved.
So there's no way to do the directory delete inside the exception
handling cleanly.
But we can handle it similarly to the transaction itself: use an
object with a destructor.
manifest hash: fc38550a20d64d08333f256bbedc312493c1390b
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (GNU/Linux)
iD8DBQFCxDT2ywK+sNU5EO8RAjikAJ0Tej56rAutxQDfYzVbFGtT1sEC5ACgmVds
/fwdQyHn+FwshugqXLemUaM=
=3f78
-----END PGP SIGNATURE-----
author | mpm@selenic.com |
---|---|
date | Thu, 30 Jun 2005 10:07:50 -0800 |
parents | ab0d1bfeee7c |
children | c15b4bc0a11c eda4c32c167a |
rev | line source |
---|---|
238
3b92f8fe47ae
hgweb.py: kill #! line, clean up copyright notice
mpm@selenic.com
parents:
222
diff
changeset
|
1 # hgweb.py - web interface to a mercurial repository |
131 | 2 # |
238
3b92f8fe47ae
hgweb.py: kill #! line, clean up copyright notice
mpm@selenic.com
parents:
222
diff
changeset
|
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> |
3b92f8fe47ae
hgweb.py: kill #! line, clean up copyright notice
mpm@selenic.com
parents:
222
diff
changeset
|
4 # Copyright 2005 Matt Mackall <mpm@selenic.com> |
131 | 5 # |
6 # This software may be used and distributed according to the terms | |
7 # of the GNU General Public License, incorporated herein by reference. | |
8 | |
9 import os, cgi, time, re, difflib, sys, zlib | |
138 | 10 from mercurial.hg import * |
215 | 11 from mercurial.ui import * |
138 | 12 |
157
2653740d8118
Install the templates where they can be found by hgweb.py
mpm@selenic.com
parents:
156
diff
changeset
|
13 def templatepath(): |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
14 for f in "templates", "../templates": |
157
2653740d8118
Install the templates where they can be found by hgweb.py
mpm@selenic.com
parents:
156
diff
changeset
|
15 p = os.path.join(os.path.dirname(__file__), f) |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
16 if os.path.isdir(p): return p |
157
2653740d8118
Install the templates where they can be found by hgweb.py
mpm@selenic.com
parents:
156
diff
changeset
|
17 |
138 | 18 def age(t): |
19 def plural(t, c): | |
20 if c == 1: return t | |
21 return t + "s" | |
22 def fmt(t, c): | |
23 return "%d %s" % (c, plural(t, c)) | |
24 | |
25 now = time.time() | |
26 delta = max(1, int(now - t)) | |
27 | |
28 scales = [["second", 1], | |
29 ["minute", 60], | |
30 ["hour", 3600], | |
31 ["day", 3600 * 24], | |
32 ["week", 3600 * 24 * 7], | |
33 ["month", 3600 * 24 * 30], | |
34 ["year", 3600 * 24 * 365]] | |
35 | |
36 scales.reverse() | |
37 | |
38 for t, s in scales: | |
39 n = delta / s | |
195 | 40 if n >= 2 or s == 1: return fmt(t, n) |
131 | 41 |
42 def nl2br(text): | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
43 return text.replace('\n', '<br/>\n') |
131 | 44 |
45 def obfuscate(text): | |
533
b8404005d6d5
[PATCH] Generate correctly XML entities for obfuscated user
mpm@selenic.com
parents:
515
diff
changeset
|
46 return ''.join([ '&#%d;' % ord(c) for c in text ]) |
138 | 47 |
48 def up(p): | |
49 if p[0] != "/": p = "/" + p | |
50 if p[-1] == "/": p = p[:-1] | |
51 up = os.path.dirname(p) | |
52 if up == "/": | |
53 return "/" | |
54 return up + "/" | |
131 | 55 |
56 def httphdr(type): | |
57 print 'Content-type: %s\n' % type | |
58 | |
135 | 59 def write(*things): |
60 for thing in things: | |
61 if hasattr(thing, "__iter__"): | |
62 for part in thing: | |
63 write(part) | |
64 else: | |
65 sys.stdout.write(str(thing)) | |
66 | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
67 def template(tmpl, filters = {}, **map): |
138 | 68 while tmpl: |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
69 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl) |
138 | 70 if m: |
71 yield tmpl[:m.start(0)] | |
72 v = map.get(m.group(1), "") | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
73 v = callable(v) and v() or v |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
74 |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
75 fl = m.group(2) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
76 if fl: |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
77 for f in fl.split("|")[1:]: |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
78 v = filters[f](v) |
515 | 79 |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
80 yield v |
138 | 81 tmpl = tmpl[m.end(0):] |
82 else: | |
83 yield tmpl | |
84 return | |
85 | |
86 class templater: | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
87 def __init__(self, mapfile, filters = {}): |
138 | 88 self.cache = {} |
89 self.map = {} | |
90 self.base = os.path.dirname(mapfile) | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
91 self.filters = filters |
515 | 92 |
138 | 93 for l in file(mapfile): |
94 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l) | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
95 if m: |
138 | 96 self.cache[m.group(1)] = m.group(2) |
97 else: | |
98 m = re.match(r'(\S+)\s*=\s*(\S+)', l) | |
99 if m: | |
100 self.map[m.group(1)] = os.path.join(self.base, m.group(2)) | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
101 else: |
138 | 102 raise "unknown map entry '%s'" % l |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
103 |
138 | 104 def __call__(self, t, **map): |
105 try: | |
106 tmpl = self.cache[t] | |
107 except KeyError: | |
108 tmpl = self.cache[t] = file(self.map[t]).read() | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
109 return template(tmpl, self.filters, **map) |
515 | 110 |
138 | 111 class hgweb: |
269
24e9e140485f
hgweb: shrink the default changelog list to 10
mpm@selenic.com
parents:
265
diff
changeset
|
112 maxchanges = 10 |
138 | 113 maxfiles = 10 |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
114 |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
115 def __init__(self, path, name, templates = ""): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
116 self.templates = templates or templatepath() |
138 | 117 self.reponame = name |
258 | 118 self.path = path |
119 self.mtime = -1 | |
197 | 120 self.viewonly = 0 |
131 | 121 |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
122 self.filters = { |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
123 "escape": cgi.escape, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
124 "age": age, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
125 "date": (lambda x: time.asctime(time.gmtime(x))), |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
126 "addbreaks": nl2br, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
127 "obfuscate": obfuscate, |
375 | 128 "short": (lambda x: x[:12]), |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
129 "firstline": (lambda x: x.splitlines(1)[0]), |
359 | 130 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--") |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
131 } |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
132 |
258 | 133 def refresh(self): |
134 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i")) | |
135 if s.st_mtime != self.mtime: | |
322 | 136 self.mtime = s.st_mtime |
258 | 137 self.repo = repository(ui(), self.path) |
138 | |
138 | 139 def date(self, cs): |
140 return time.asctime(time.gmtime(float(cs[2].split(' ')[0]))) | |
141 | |
142 def listfiles(self, files, mf): | |
143 for f in files[:self.maxfiles]: | |
144 yield self.t("filenodelink", node = hex(mf[f]), file = f) | |
145 if len(files) > self.maxfiles: | |
146 yield self.t("fileellipses") | |
147 | |
148 def listfilediffs(self, files, changeset): | |
149 for f in files[:self.maxfiles]: | |
150 yield self.t("filedifflink", node = hex(changeset), file = f) | |
151 if len(files) > self.maxfiles: | |
152 yield self.t("fileellipses") | |
153 | |
156 | 154 def parent(self, t1, node=nullid, rev=-1, **args): |
142 | 155 if node != hex(nullid): |
156 | 156 yield self.t(t1, node = node, rev = rev, **args) |
142 | 157 |
138 | 158 def diff(self, node1, node2, files): |
159 def filterfiles(list, files): | |
160 l = [ x for x in list if x in files ] | |
515 | 161 |
138 | 162 for f in files: |
163 if f[-1] != os.sep: f += os.sep | |
164 l += [ x for x in list if x.startswith(f) ] | |
165 return l | |
131 | 166 |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
167 parity = [0] |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
168 def diffblock(diff, f, fn): |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
169 yield self.t("diffblock", |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
170 lines = prettyprintlines(diff), |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
171 parity = parity[0], |
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
172 file = f, |
369
09f24af48b82
hgweb: fix deleted file in changeset key error
mpm@selenic.com
parents:
366
diff
changeset
|
173 filenode = hex(fn or nullid)) |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
174 parity[0] = 1 - parity[0] |
515 | 175 |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
176 def prettyprintlines(diff): |
138 | 177 for l in diff.splitlines(1): |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
178 if l.startswith('+'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
179 yield self.t("difflineplus", line = l) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
180 elif l.startswith('-'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
181 yield self.t("difflineminus", line = l) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
182 elif l.startswith('@'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
183 yield self.t("difflineat", line = l) |
138 | 184 else: |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
185 yield self.t("diffline", line = l) |
131 | 186 |
138 | 187 r = self.repo |
188 cl = r.changelog | |
189 mf = r.manifest | |
190 change1 = cl.read(node1) | |
191 change2 = cl.read(node2) | |
192 mmap1 = mf.read(change1[0]) | |
193 mmap2 = mf.read(change2[0]) | |
194 date1 = self.date(change1) | |
195 date2 = self.date(change2) | |
131 | 196 |
138 | 197 c, a, d = r.diffrevs(node1, node2) |
198 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d)) | |
131 | 199 |
138 | 200 for f in c: |
201 to = r.file(f).read(mmap1[f]) | |
202 tn = r.file(f).read(mmap2[f]) | |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
203 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) |
138 | 204 for f in a: |
265
7ca05593bd30
hgweb: fix non-existent source or destination for diff
mpm@selenic.com
parents:
258
diff
changeset
|
205 to = None |
138 | 206 tn = r.file(f).read(mmap2[f]) |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
207 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) |
138 | 208 for f in d: |
209 to = r.file(f).read(mmap1[f]) | |
265
7ca05593bd30
hgweb: fix non-existent source or destination for diff
mpm@selenic.com
parents:
258
diff
changeset
|
210 tn = None |
172
e9b1147db448
hgweb: alternating colors for multifile diffs
mpm@selenic.com
parents:
168
diff
changeset
|
211 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn) |
131 | 212 |
142 | 213 def header(self): |
214 yield self.t("header", repo = self.reponame) | |
215 | |
216 def footer(self): | |
217 yield self.t("footer", repo = self.reponame) | |
218 | |
180 | 219 def changelog(self, pos): |
138 | 220 def changenav(): |
221 def seq(factor = 1): | |
222 yield 1 * factor | |
173
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
223 yield 3 * factor |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
224 #yield 5 * factor |
138 | 225 for f in seq(factor * 10): |
226 yield f | |
131 | 227 |
173
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
228 l = [] |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
229 for f in seq(): |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
230 if f < self.maxchanges / 2: continue |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
231 if f > count: break |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
232 r = "%d" % f |
351 | 233 if pos + f < count: l.append(("+" + r, pos + f)) |
234 if pos - f >= 0: l.insert(0, ("-" + r, pos - f)) | |
173
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
235 |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
236 yield self.t("naventry", rev = 0, label="(0)") |
515 | 237 |
173
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
238 for label, rev in l: |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
239 yield self.t("naventry", label = label, rev = rev) |
8da1df932c16
hgweb: make navigation of changesets a bit nicer
mpm@selenic.com
parents:
172
diff
changeset
|
240 |
198
c88ef31fb5c0
hgweb: fix tip so that it doesn't need refreshing
mpm@selenic.com
parents:
197
diff
changeset
|
241 yield self.t("naventry", label="tip") |
131 | 242 |
138 | 243 def changelist(): |
142 | 244 parity = (start - end) & 1 |
138 | 245 cl = self.repo.changelog |
246 l = [] # build a list in forward order for efficiency | |
351 | 247 for i in range(start, end): |
138 | 248 n = cl.node(i) |
249 changes = cl.read(n) | |
250 hn = hex(n) | |
251 p1, p2 = cl.parents(n) | |
252 t = float(changes[2].split(' ')[0]) | |
131 | 253 |
138 | 254 l.insert(0, self.t( |
255 'changelogentry', | |
142 | 256 parity = parity, |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
257 author = changes[1], |
142 | 258 parent1 = self.parent("changelogparent", |
259 hex(p1), cl.rev(p1)), | |
260 parent2 = self.parent("changelogparent", | |
261 hex(p2), cl.rev(p2)), | |
138 | 262 p1 = hex(p1), p2 = hex(p2), |
263 p1rev = cl.rev(p1), p2rev = cl.rev(p2), | |
264 manifest = hex(changes[0]), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
265 desc = changes[4], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
266 date = t, |
138 | 267 files = self.listfilediffs(changes[3], n), |
268 rev = i, | |
269 node = hn)) | |
142 | 270 parity = 1 - parity |
138 | 271 |
272 yield l | |
131 | 273 |
168 | 274 cl = self.repo.changelog |
275 mf = cl.read(cl.tip())[0] | |
276 count = cl.count() | |
351 | 277 start = max(0, pos - self.maxchanges + 1) |
278 end = min(count, start + self.maxchanges) | |
279 pos = end - 1 | |
138 | 280 |
142 | 281 yield self.t('changelog', |
282 header = self.header(), | |
283 footer = self.footer(), | |
284 repo = self.reponame, | |
285 changenav = changenav, | |
168 | 286 manifest = hex(mf), |
142 | 287 rev = pos, changesets = count, entries = changelist) |
131 | 288 |
138 | 289 def changeset(self, nodeid): |
290 n = bin(nodeid) | |
291 cl = self.repo.changelog | |
292 changes = cl.read(n) | |
293 p1, p2 = cl.parents(n) | |
294 p1rev, p2rev = cl.rev(p1), cl.rev(p2) | |
295 t = float(changes[2].split(' ')[0]) | |
515 | 296 |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
297 files = [] |
138 | 298 mf = self.repo.manifest.read(changes[0]) |
131 | 299 for f in changes[3]: |
138 | 300 files.append(self.t("filenodelink", |
369
09f24af48b82
hgweb: fix deleted file in changeset key error
mpm@selenic.com
parents:
366
diff
changeset
|
301 filenode = hex(mf.get(f, nullid)), file = f)) |
138 | 302 |
303 def diff(): | |
304 yield self.diff(p1, n, changes[3]) | |
131 | 305 |
138 | 306 yield self.t('changeset', |
142 | 307 header = self.header(), |
308 footer = self.footer(), | |
309 repo = self.reponame, | |
138 | 310 diff = diff, |
311 rev = cl.rev(n), | |
312 node = nodeid, | |
142 | 313 parent1 = self.parent("changesetparent", |
314 hex(p1), cl.rev(p1)), | |
315 parent2 = self.parent("changesetparent", | |
316 hex(p2), cl.rev(p2)), | |
138 | 317 p1 = hex(p1), p2 = hex(p2), |
318 p1rev = cl.rev(p1), p2rev = cl.rev(p2), | |
319 manifest = hex(changes[0]), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
320 author = changes[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
321 desc = changes[4], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
322 date = t, |
138 | 323 files = files) |
131 | 324 |
138 | 325 def filelog(self, f, filenode): |
326 cl = self.repo.changelog | |
327 fl = self.repo.file(f) | |
328 count = fl.count() | |
329 | |
330 def entries(): | |
331 l = [] | |
142 | 332 parity = (count - 1) & 1 |
515 | 333 |
138 | 334 for i in range(count): |
335 | |
336 n = fl.node(i) | |
337 lr = fl.linkrev(n) | |
338 cn = cl.node(lr) | |
339 cs = cl.read(cl.node(lr)) | |
340 p1, p2 = fl.parents(n) | |
341 t = float(cs[2].split(' ')[0]) | |
133
fb84d3e71042
added template support for some hgweb output, also, template files for
jake@edge2.net
parents:
132
diff
changeset
|
342 |
138 | 343 l.insert(0, self.t("filelogentry", |
142 | 344 parity = parity, |
138 | 345 filenode = hex(n), |
346 filerev = i, | |
347 file = f, | |
348 node = hex(cn), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
349 author = cs[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
350 date = t, |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
351 desc = cs[4], |
138 | 352 p1 = hex(p1), p2 = hex(p2), |
353 p1rev = fl.rev(p1), p2rev = fl.rev(p2))) | |
142 | 354 parity = 1 - parity |
138 | 355 |
356 yield l | |
357 | |
358 yield self.t("filelog", | |
142 | 359 header = self.header(), |
360 footer = self.footer(), | |
361 repo = self.reponame, | |
138 | 362 file = f, |
363 filenode = filenode, | |
364 entries = entries) | |
131 | 365 |
138 | 366 def filerevision(self, f, node): |
367 fl = self.repo.file(f) | |
368 n = bin(node) | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
369 text = fl.read(n) |
138 | 370 changerev = fl.linkrev(n) |
371 cl = self.repo.changelog | |
372 cn = cl.node(changerev) | |
373 cs = cl.read(cn) | |
374 p1, p2 = fl.parents(n) | |
375 t = float(cs[2].split(' ')[0]) | |
376 mfn = cs[0] | |
142 | 377 |
378 def lines(): | |
379 for l, t in enumerate(text.splitlines(1)): | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
380 yield self.t("fileline", line = t, |
142 | 381 linenumber = "% 6d" % (l + 1), |
382 parity = l & 1) | |
359 | 383 |
138 | 384 yield self.t("filerevision", file = f, |
142 | 385 header = self.header(), |
386 footer = self.footer(), | |
387 repo = self.reponame, | |
138 | 388 filenode = node, |
389 path = up(f), | |
142 | 390 text = lines(), |
138 | 391 rev = changerev, |
392 node = hex(cn), | |
393 manifest = hex(mfn), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
394 author = cs[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
395 date = t, |
142 | 396 parent1 = self.parent("filerevparent", |
156 | 397 hex(p1), fl.rev(p1), file=f), |
142 | 398 parent2 = self.parent("filerevparent", |
156 | 399 hex(p2), fl.rev(p2), file=f), |
138 | 400 p1 = hex(p1), p2 = hex(p2), |
359 | 401 permissions = self.repo.manifest.readflags(mfn)[f], |
138 | 402 p1rev = fl.rev(p1), p2rev = fl.rev(p2)) |
403 | |
404 def fileannotate(self, f, node): | |
405 bcache = {} | |
406 ncache = {} | |
407 fl = self.repo.file(f) | |
408 n = bin(node) | |
409 changerev = fl.linkrev(n) | |
410 | |
411 cl = self.repo.changelog | |
412 cn = cl.node(changerev) | |
413 cs = cl.read(cn) | |
414 p1, p2 = fl.parents(n) | |
415 t = float(cs[2].split(' ')[0]) | |
416 mfn = cs[0] | |
131 | 417 |
138 | 418 def annotate(): |
142 | 419 parity = 1 |
420 last = None | |
138 | 421 for r, l in fl.annotate(n): |
422 try: | |
423 cnode = ncache[r] | |
424 except KeyError: | |
425 cnode = ncache[r] = self.repo.changelog.node(r) | |
515 | 426 |
138 | 427 try: |
428 name = bcache[r] | |
429 except KeyError: | |
430 cl = self.repo.changelog.read(cnode) | |
431 name = cl[1] | |
432 f = name.find('@') | |
433 if f >= 0: | |
434 name = name[:f] | |
534
ab0d1bfeee7c
[PATCH] Handle 'name firstname <email@server>' correctly in annotate
mpm@selenic.com
parents:
533
diff
changeset
|
435 f = name.find('<') |
ab0d1bfeee7c
[PATCH] Handle 'name firstname <email@server>' correctly in annotate
mpm@selenic.com
parents:
533
diff
changeset
|
436 if f >= 0: |
ab0d1bfeee7c
[PATCH] Handle 'name firstname <email@server>' correctly in annotate
mpm@selenic.com
parents:
533
diff
changeset
|
437 name = name[f+1:] |
138 | 438 bcache[r] = name |
131 | 439 |
142 | 440 if last != cnode: |
441 parity = 1 - parity | |
442 last = cnode | |
443 | |
138 | 444 yield self.t("annotateline", |
142 | 445 parity = parity, |
138 | 446 node = hex(cnode), |
447 rev = r, | |
448 author = name, | |
449 file = f, | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
450 line = l) |
138 | 451 |
452 yield self.t("fileannotate", | |
142 | 453 header = self.header(), |
454 footer = self.footer(), | |
455 repo = self.reponame, | |
138 | 456 file = f, |
457 filenode = node, | |
458 annotate = annotate, | |
459 path = up(f), | |
460 rev = changerev, | |
461 node = hex(cn), | |
462 manifest = hex(mfn), | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
463 author = cs[1], |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
464 date = t, |
156 | 465 parent1 = self.parent("fileannotateparent", |
466 hex(p1), fl.rev(p1), file=f), | |
467 parent2 = self.parent("fileannotateparent", | |
468 hex(p2), fl.rev(p2), file=f), | |
138 | 469 p1 = hex(p1), p2 = hex(p2), |
359 | 470 permissions = self.repo.manifest.readflags(mfn)[f], |
138 | 471 p1rev = fl.rev(p1), p2rev = fl.rev(p2)) |
136 | 472 |
138 | 473 def manifest(self, mnode, path): |
474 mf = self.repo.manifest.read(bin(mnode)) | |
475 rev = self.repo.manifest.rev(bin(mnode)) | |
476 node = self.repo.changelog.node(rev) | |
359 | 477 mff=self.repo.manifest.readflags(bin(mnode)) |
138 | 478 |
479 files = {} | |
515 | 480 |
138 | 481 p = path[1:] |
482 l = len(p) | |
131 | 483 |
138 | 484 for f,n in mf.items(): |
485 if f[:l] != p: | |
486 continue | |
487 remain = f[l:] | |
488 if "/" in remain: | |
489 short = remain[:remain.find("/") + 1] # bleah | |
142 | 490 files[short] = (f, None) |
138 | 491 else: |
492 short = os.path.basename(remain) | |
493 files[short] = (f, n) | |
131 | 494 |
138 | 495 def filelist(): |
142 | 496 parity = 0 |
138 | 497 fl = files.keys() |
498 fl.sort() | |
499 for f in fl: | |
500 full, fnode = files[f] | |
142 | 501 if fnode: |
502 yield self.t("manifestfileentry", | |
503 file = full, | |
504 manifest = mnode, | |
505 filenode = hex(fnode), | |
506 parity = parity, | |
515 | 507 basename = f, |
359 | 508 permissions = mff[full]) |
142 | 509 else: |
510 yield self.t("manifestdirentry", | |
511 parity = parity, | |
512 path = os.path.join(path, f), | |
513 manifest = mnode, basename = f[:-1]) | |
514 parity = 1 - parity | |
138 | 515 |
516 yield self.t("manifest", | |
142 | 517 header = self.header(), |
518 footer = self.footer(), | |
519 repo = self.reponame, | |
138 | 520 manifest = mnode, |
521 rev = rev, | |
522 node = hex(node), | |
523 path = path, | |
524 up = up(path), | |
142 | 525 entries = filelist) |
131 | 526 |
168 | 527 def tags(self): |
528 cl = self.repo.changelog | |
529 mf = cl.read(cl.tip())[0] | |
530 | |
343 | 531 i = self.repo.tagslist() |
532 i.reverse() | |
168 | 533 |
534 def entries(): | |
535 parity = 0 | |
536 for k,n in i: | |
537 yield self.t("tagentry", | |
538 parity = parity, | |
539 tag = k, | |
540 node = hex(n)) | |
541 parity = 1 - parity | |
542 | |
543 yield self.t("tags", | |
544 header = self.header(), | |
545 footer = self.footer(), | |
546 repo = self.reponame, | |
547 manifest = hex(mf), | |
548 entries = entries) | |
549 | |
138 | 550 def filediff(self, file, changeset): |
551 n = bin(changeset) | |
552 cl = self.repo.changelog | |
553 p1 = cl.parents(n)[0] | |
554 cs = cl.read(n) | |
555 mf = self.repo.manifest.read(cs[0]) | |
515 | 556 |
138 | 557 def diff(): |
558 yield self.diff(p1, n, file) | |
131 | 559 |
138 | 560 yield self.t("filediff", |
142 | 561 header = self.header(), |
562 footer = self.footer(), | |
563 repo = self.reponame, | |
138 | 564 file = file, |
376
fadc9e126369
hgweb: fix deleted file in filediff key error
Thomas Arendsen Hein <thomas@intevation.de>
parents:
369
diff
changeset
|
565 filenode = hex(mf.get(file, nullid)), |
138 | 566 node = changeset, |
567 rev = self.repo.changelog.rev(n), | |
568 p1 = hex(p1), | |
569 p1rev = self.repo.changelog.rev(p1), | |
570 diff = diff) | |
515 | 571 |
138 | 572 # add tags to things |
573 # tags -> list of changesets corresponding to tags | |
574 # find tag, changeset, file | |
131 | 575 |
132 | 576 def run(self): |
258 | 577 self.refresh() |
132 | 578 args = cgi.parse() |
579 | |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
580 m = os.path.join(self.templates, "map") |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
581 if args.has_key('style'): |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
582 b = os.path.basename("map-" + args['style'][0]) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
583 p = os.path.join(self.templates, b) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
584 if os.path.isfile(p): m = p |
515 | 585 |
201
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
586 self.t = templater(m, self.filters) |
f918a6fa2572
hgweb: add template filters, template style maps, and raw pages
mpm@selenic.com
parents:
198
diff
changeset
|
587 |
138 | 588 if not args.has_key('cmd') or args['cmd'][0] == 'changelog': |
351 | 589 hi = self.repo.changelog.count() - 1 |
153
e8a360cd5a9f
changed pos to rev for changelog cmd, changed & to ;
jake@edge2.net
parents:
142
diff
changeset
|
590 if args.has_key('rev'): |
165 | 591 hi = args['rev'][0] |
166
39624c47060f
hgweb: don't blow up on search for unknown keys
mpm@selenic.com
parents:
165
diff
changeset
|
592 try: |
39624c47060f
hgweb: don't blow up on search for unknown keys
mpm@selenic.com
parents:
165
diff
changeset
|
593 hi = self.repo.changelog.rev(self.repo.lookup(hi)) |
351 | 594 except KeyError: pass |
131 | 595 |
138 | 596 write(self.changelog(hi)) |
515 | 597 |
138 | 598 elif args['cmd'][0] == 'changeset': |
599 write(self.changeset(args['node'][0])) | |
600 | |
601 elif args['cmd'][0] == 'manifest': | |
602 write(self.manifest(args['manifest'][0], args['path'][0])) | |
603 | |
168 | 604 elif args['cmd'][0] == 'tags': |
605 write(self.tags()) | |
606 | |
138 | 607 elif args['cmd'][0] == 'filediff': |
608 write(self.filediff(args['file'][0], args['node'][0])) | |
131 | 609 |
132 | 610 elif args['cmd'][0] == 'file': |
138 | 611 write(self.filerevision(args['file'][0], args['filenode'][0])) |
131 | 612 |
138 | 613 elif args['cmd'][0] == 'annotate': |
614 write(self.fileannotate(args['file'][0], args['filenode'][0])) | |
131 | 615 |
138 | 616 elif args['cmd'][0] == 'filelog': |
617 write(self.filelog(args['file'][0], args['filenode'][0])) | |
136 | 618 |
222 | 619 elif args['cmd'][0] == 'heads': |
620 httphdr("text/plain") | |
621 h = self.repo.heads() | |
622 sys.stdout.write(" ".join(map(hex, h)) + "\n") | |
623 | |
132 | 624 elif args['cmd'][0] == 'branches': |
625 httphdr("text/plain") | |
626 nodes = [] | |
627 if args.has_key('nodes'): | |
138 | 628 nodes = map(bin, args['nodes'][0].split(" ")) |
629 for b in self.repo.branches(nodes): | |
630 sys.stdout.write(" ".join(map(hex, b)) + "\n") | |
131 | 631 |
132 | 632 elif args['cmd'][0] == 'between': |
633 httphdr("text/plain") | |
634 nodes = [] | |
635 if args.has_key('pairs'): | |
138 | 636 pairs = [ map(bin, p.split("-")) |
132 | 637 for p in args['pairs'][0].split(" ") ] |
138 | 638 for b in self.repo.between(pairs): |
639 sys.stdout.write(" ".join(map(hex, b)) + "\n") | |
132 | 640 |
641 elif args['cmd'][0] == 'changegroup': | |
642 httphdr("application/hg-changegroup") | |
643 nodes = [] | |
197 | 644 if self.viewonly: |
645 return | |
646 | |
132 | 647 if args.has_key('roots'): |
138 | 648 nodes = map(bin, args['roots'][0].split(" ")) |
131 | 649 |
132 | 650 z = zlib.compressobj() |
138 | 651 for chunk in self.repo.changegroup(nodes): |
132 | 652 sys.stdout.write(z.compress(chunk)) |
653 | |
654 sys.stdout.write(z.flush()) | |
131 | 655 |
132 | 656 else: |
138 | 657 write(self.t("error")) |
131 | 658 |
158
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
659 def server(path, name, templates, address, port): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
660 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
661 import BaseHTTPServer |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
662 import sys, os |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
663 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
664 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
665 def do_POST(self): |
271 | 666 try: |
667 self.do_hgweb() | |
668 except socket.error, inst: | |
669 if inst.args[0] != 32: raise | |
158
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
670 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
671 def do_GET(self): |
271 | 672 self.do_POST() |
158
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
673 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
674 def do_hgweb(self): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
675 query = "" |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
676 p = self.path.find("?") |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
677 if p: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
678 query = self.path[p + 1:] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
679 query = query.replace('+', ' ') |
515 | 680 |
158
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
681 env = {} |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
682 env['GATEWAY_INTERFACE'] = 'CGI/1.1' |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
683 env['REQUEST_METHOD'] = self.command |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
684 if query: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
685 env['QUERY_STRING'] = query |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
686 host = self.address_string() |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
687 if host != self.client_address[0]: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
688 env['REMOTE_HOST'] = host |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
689 env['REMOTE_ADDR'] = self.client_address[0] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
690 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
691 if self.headers.typeheader is None: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
692 env['CONTENT_TYPE'] = self.headers.type |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
693 else: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
694 env['CONTENT_TYPE'] = self.headers.typeheader |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
695 length = self.headers.getheader('content-length') |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
696 if length: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
697 env['CONTENT_LENGTH'] = length |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
698 accept = [] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
699 for line in self.headers.getallmatchingheaders('accept'): |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
700 if line[:1] in "\t\n\r ": |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
701 accept.append(line.strip()) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
702 else: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
703 accept = accept + line[7:].split(',') |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
704 env['HTTP_ACCEPT'] = ','.join(accept) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
705 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
706 os.environ.update(env) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
707 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
708 save = sys.argv, sys.stdin, sys.stdout, sys.stderr |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
709 try: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
710 sys.stdin = self.rfile |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
711 sys.stdout = self.wfile |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
712 sys.argv = ["hgweb.py"] |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
713 if '=' not in query: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
714 sys.argv.append(query) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
715 self.send_response(200, "Script output follows") |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
716 hg.run() |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
717 finally: |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
718 sys.argv, sys.stdin, sys.stdout, sys.stderr = save |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
719 |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
720 hg = hgweb(path, name, templates) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
721 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler) |
be7103467d2e
Add 'hg serve' command for stand-alone server
mpm@selenic.com
parents:
157
diff
changeset
|
722 httpd.serve_forever() |