Show More
@@ -10,9 +10,11 b' from node import *' | |||||
10 | from i18n import gettext as _ |
|
10 | from i18n import gettext as _ | |
11 | demandload(globals(), "os re sys signal shutil imp urllib pdb") |
|
11 | demandload(globals(), "os re sys signal shutil imp urllib pdb") | |
12 | demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo") |
|
12 | demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo") | |
13 |
demandload(globals(), "fnmatch |
|
13 | demandload(globals(), "fnmatch mdiff random signal tempfile time") | |
14 | demandload(globals(), "traceback errno socket version struct atexit sets bz2") |
|
14 | demandload(globals(), "traceback errno socket version struct atexit sets bz2") | |
15 | demandload(globals(), "archival changegroup") |
|
15 | demandload(globals(), "archival changegroup") | |
|
16 | demandload(globals(), "mercurial.hgweb.server:create_server") | |||
|
17 | demandload(globals(), "mercurial.hgweb:hgweb,hgwebdir") | |||
16 |
|
18 | |||
17 | class UnknownCommand(Exception): |
|
19 | class UnknownCommand(Exception): | |
18 | """Exception raised if command is not in the command table.""" |
|
20 | """Exception raised if command is not in the command table.""" | |
@@ -2542,7 +2544,7 b' def serve(ui, repo, **opts):' | |||||
2542 | os._exit(0) |
|
2544 | os._exit(0) | |
2543 |
|
2545 | |||
2544 | try: |
|
2546 | try: | |
2545 |
httpd = |
|
2547 | httpd = create_server(ui, repo, hgwebdir, hgweb) | |
2546 | except socket.error, inst: |
|
2548 | except socket.error, inst: | |
2547 | raise util.Abort(_('cannot start server: ') + inst.args[1]) |
|
2549 | raise util.Abort(_('cannot start server: ') + inst.args[1]) | |
2548 |
|
2550 |
This diff has been collapsed as it changes many lines, (981 lines changed) Show them Hide them | |||||
@@ -6,983 +6,6 b'' | |||||
6 | # This software may be used and distributed according to the terms |
|
6 | # This software may be used and distributed according to the terms | |
7 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | # of the GNU General Public License, incorporated herein by reference. | |
8 |
|
8 | |||
9 | import os, cgi, sys |
|
|||
10 | import mimetypes |
|
|||
11 | from mercurial.demandload import demandload |
|
9 | from mercurial.demandload import demandload | |
12 | demandload(globals(), "time re socket zlib errno ConfigParser tempfile") |
|
10 | demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb") | |
13 |
demandload(globals(), "mercurial |
|
11 | demandload(globals(), "mercurial.hgweb.hgwebdir_mod:hgwebdir") | |
14 | demandload(globals(), "mercurial.hgweb.request:hgrequest") |
|
|||
15 | demandload(globals(), "mercurial.hgweb.server:create_server") |
|
|||
16 | from mercurial.node import * |
|
|||
17 | from mercurial.i18n import gettext as _ |
|
|||
18 |
|
||||
19 | def up(p): |
|
|||
20 | if p[0] != "/": |
|
|||
21 | p = "/" + p |
|
|||
22 | if p[-1] == "/": |
|
|||
23 | p = p[:-1] |
|
|||
24 | up = os.path.dirname(p) |
|
|||
25 | if up == "/": |
|
|||
26 | return "/" |
|
|||
27 | return up + "/" |
|
|||
28 |
|
||||
29 | def get_mtime(repo_path): |
|
|||
30 | hg_path = os.path.join(repo_path, ".hg") |
|
|||
31 | cl_path = os.path.join(hg_path, "00changelog.i") |
|
|||
32 | if os.path.exists(os.path.join(cl_path)): |
|
|||
33 | return os.stat(cl_path).st_mtime |
|
|||
34 | else: |
|
|||
35 | return os.stat(hg_path).st_mtime |
|
|||
36 |
|
||||
37 | def staticfile(directory, fname): |
|
|||
38 | """return a file inside directory with guessed content-type header |
|
|||
39 |
|
||||
40 | fname always uses '/' as directory separator and isn't allowed to |
|
|||
41 | contain unusual path components. |
|
|||
42 | Content-type is guessed using the mimetypes module. |
|
|||
43 | Return an empty string if fname is illegal or file not found. |
|
|||
44 |
|
||||
45 | """ |
|
|||
46 | parts = fname.split('/') |
|
|||
47 | path = directory |
|
|||
48 | for part in parts: |
|
|||
49 | if (part in ('', os.curdir, os.pardir) or |
|
|||
50 | os.sep in part or os.altsep is not None and os.altsep in part): |
|
|||
51 | return "" |
|
|||
52 | path = os.path.join(path, part) |
|
|||
53 | try: |
|
|||
54 | os.stat(path) |
|
|||
55 | ct = mimetypes.guess_type(path)[0] or "text/plain" |
|
|||
56 | return "Content-type: %s\n\n%s" % (ct, file(path).read()) |
|
|||
57 | except (TypeError, OSError): |
|
|||
58 | # illegal fname or unreadable file |
|
|||
59 | return "" |
|
|||
60 |
|
||||
61 | class hgweb(object): |
|
|||
62 | def __init__(self, repo, name=None): |
|
|||
63 | if type(repo) == type(""): |
|
|||
64 | self.repo = hg.repository(ui.ui(), repo) |
|
|||
65 | else: |
|
|||
66 | self.repo = repo |
|
|||
67 |
|
||||
68 | self.mtime = -1 |
|
|||
69 | self.reponame = name |
|
|||
70 | self.archives = 'zip', 'gz', 'bz2' |
|
|||
71 |
|
||||
72 | def refresh(self): |
|
|||
73 | mtime = get_mtime(self.repo.root) |
|
|||
74 | if mtime != self.mtime: |
|
|||
75 | self.mtime = mtime |
|
|||
76 | self.repo = hg.repository(self.repo.ui, self.repo.root) |
|
|||
77 | self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10)) |
|
|||
78 | self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10)) |
|
|||
79 | self.allowpull = self.repo.ui.configbool("web", "allowpull", True) |
|
|||
80 |
|
||||
81 | def archivelist(self, nodeid): |
|
|||
82 | for i in self.archives: |
|
|||
83 | if self.repo.ui.configbool("web", "allow" + i, False): |
|
|||
84 | yield {"type" : i, "node" : nodeid, "url": ""} |
|
|||
85 |
|
||||
86 | def listfiles(self, files, mf): |
|
|||
87 | for f in files[:self.maxfiles]: |
|
|||
88 | yield self.t("filenodelink", node=hex(mf[f]), file=f) |
|
|||
89 | if len(files) > self.maxfiles: |
|
|||
90 | yield self.t("fileellipses") |
|
|||
91 |
|
||||
92 | def listfilediffs(self, files, changeset): |
|
|||
93 | for f in files[:self.maxfiles]: |
|
|||
94 | yield self.t("filedifflink", node=hex(changeset), file=f) |
|
|||
95 | if len(files) > self.maxfiles: |
|
|||
96 | yield self.t("fileellipses") |
|
|||
97 |
|
||||
98 | def siblings(self, siblings=[], rev=None, hiderev=None, **args): |
|
|||
99 | if not rev: |
|
|||
100 | rev = lambda x: "" |
|
|||
101 | siblings = [s for s in siblings if s != nullid] |
|
|||
102 | if len(siblings) == 1 and rev(siblings[0]) == hiderev: |
|
|||
103 | return |
|
|||
104 | for s in siblings: |
|
|||
105 | yield dict(node=hex(s), rev=rev(s), **args) |
|
|||
106 |
|
||||
107 | def renamelink(self, fl, node): |
|
|||
108 | r = fl.renamed(node) |
|
|||
109 | if r: |
|
|||
110 | return [dict(file=r[0], node=hex(r[1]))] |
|
|||
111 | return [] |
|
|||
112 |
|
||||
113 | def showtag(self, t1, node=nullid, **args): |
|
|||
114 | for t in self.repo.nodetags(node): |
|
|||
115 | yield self.t(t1, tag=t, **args) |
|
|||
116 |
|
||||
117 | def diff(self, node1, node2, files): |
|
|||
118 | def filterfiles(filters, files): |
|
|||
119 | l = [x for x in files if x in filters] |
|
|||
120 |
|
||||
121 | for t in filters: |
|
|||
122 | if t and t[-1] != os.sep: |
|
|||
123 | t += os.sep |
|
|||
124 | l += [x for x in files if x.startswith(t)] |
|
|||
125 | return l |
|
|||
126 |
|
||||
127 | parity = [0] |
|
|||
128 | def diffblock(diff, f, fn): |
|
|||
129 | yield self.t("diffblock", |
|
|||
130 | lines=prettyprintlines(diff), |
|
|||
131 | parity=parity[0], |
|
|||
132 | file=f, |
|
|||
133 | filenode=hex(fn or nullid)) |
|
|||
134 | parity[0] = 1 - parity[0] |
|
|||
135 |
|
||||
136 | def prettyprintlines(diff): |
|
|||
137 | for l in diff.splitlines(1): |
|
|||
138 | if l.startswith('+'): |
|
|||
139 | yield self.t("difflineplus", line=l) |
|
|||
140 | elif l.startswith('-'): |
|
|||
141 | yield self.t("difflineminus", line=l) |
|
|||
142 | elif l.startswith('@'): |
|
|||
143 | yield self.t("difflineat", line=l) |
|
|||
144 | else: |
|
|||
145 | yield self.t("diffline", line=l) |
|
|||
146 |
|
||||
147 | r = self.repo |
|
|||
148 | cl = r.changelog |
|
|||
149 | mf = r.manifest |
|
|||
150 | change1 = cl.read(node1) |
|
|||
151 | change2 = cl.read(node2) |
|
|||
152 | mmap1 = mf.read(change1[0]) |
|
|||
153 | mmap2 = mf.read(change2[0]) |
|
|||
154 | date1 = util.datestr(change1[2]) |
|
|||
155 | date2 = util.datestr(change2[2]) |
|
|||
156 |
|
||||
157 | modified, added, removed, deleted, unknown = r.changes(node1, node2) |
|
|||
158 | if files: |
|
|||
159 | modified, added, removed = map(lambda x: filterfiles(files, x), |
|
|||
160 | (modified, added, removed)) |
|
|||
161 |
|
||||
162 | diffopts = self.repo.ui.diffopts() |
|
|||
163 | showfunc = diffopts['showfunc'] |
|
|||
164 | ignorews = diffopts['ignorews'] |
|
|||
165 | for f in modified: |
|
|||
166 | to = r.file(f).read(mmap1[f]) |
|
|||
167 | tn = r.file(f).read(mmap2[f]) |
|
|||
168 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
169 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
170 | for f in added: |
|
|||
171 | to = None |
|
|||
172 | tn = r.file(f).read(mmap2[f]) |
|
|||
173 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
174 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
175 | for f in removed: |
|
|||
176 | to = r.file(f).read(mmap1[f]) |
|
|||
177 | tn = None |
|
|||
178 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
179 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
180 |
|
||||
181 | def changelog(self, pos): |
|
|||
182 | def changenav(**map): |
|
|||
183 | def seq(factor, maxchanges=None): |
|
|||
184 | if maxchanges: |
|
|||
185 | yield maxchanges |
|
|||
186 | if maxchanges >= 20 and maxchanges <= 40: |
|
|||
187 | yield 50 |
|
|||
188 | else: |
|
|||
189 | yield 1 * factor |
|
|||
190 | yield 3 * factor |
|
|||
191 | for f in seq(factor * 10): |
|
|||
192 | yield f |
|
|||
193 |
|
||||
194 | l = [] |
|
|||
195 | last = 0 |
|
|||
196 | for f in seq(1, self.maxchanges): |
|
|||
197 | if f < self.maxchanges or f <= last: |
|
|||
198 | continue |
|
|||
199 | if f > count: |
|
|||
200 | break |
|
|||
201 | last = f |
|
|||
202 | r = "%d" % f |
|
|||
203 | if pos + f < count: |
|
|||
204 | l.append(("+" + r, pos + f)) |
|
|||
205 | if pos - f >= 0: |
|
|||
206 | l.insert(0, ("-" + r, pos - f)) |
|
|||
207 |
|
||||
208 | yield {"rev": 0, "label": "(0)"} |
|
|||
209 |
|
||||
210 | for label, rev in l: |
|
|||
211 | yield {"label": label, "rev": rev} |
|
|||
212 |
|
||||
213 | yield {"label": "tip", "rev": "tip"} |
|
|||
214 |
|
||||
215 | def changelist(**map): |
|
|||
216 | parity = (start - end) & 1 |
|
|||
217 | cl = self.repo.changelog |
|
|||
218 | l = [] # build a list in forward order for efficiency |
|
|||
219 | for i in range(start, end): |
|
|||
220 | n = cl.node(i) |
|
|||
221 | changes = cl.read(n) |
|
|||
222 | hn = hex(n) |
|
|||
223 |
|
||||
224 | l.insert(0, {"parity": parity, |
|
|||
225 | "author": changes[1], |
|
|||
226 | "parent": self.siblings(cl.parents(n), cl.rev, |
|
|||
227 | cl.rev(n) - 1), |
|
|||
228 | "child": self.siblings(cl.children(n), cl.rev, |
|
|||
229 | cl.rev(n) + 1), |
|
|||
230 | "changelogtag": self.showtag("changelogtag",n), |
|
|||
231 | "manifest": hex(changes[0]), |
|
|||
232 | "desc": changes[4], |
|
|||
233 | "date": changes[2], |
|
|||
234 | "files": self.listfilediffs(changes[3], n), |
|
|||
235 | "rev": i, |
|
|||
236 | "node": hn}) |
|
|||
237 | parity = 1 - parity |
|
|||
238 |
|
||||
239 | for e in l: |
|
|||
240 | yield e |
|
|||
241 |
|
||||
242 | cl = self.repo.changelog |
|
|||
243 | mf = cl.read(cl.tip())[0] |
|
|||
244 | count = cl.count() |
|
|||
245 | start = max(0, pos - self.maxchanges + 1) |
|
|||
246 | end = min(count, start + self.maxchanges) |
|
|||
247 | pos = end - 1 |
|
|||
248 |
|
||||
249 | yield self.t('changelog', |
|
|||
250 | changenav=changenav, |
|
|||
251 | manifest=hex(mf), |
|
|||
252 | rev=pos, changesets=count, entries=changelist, |
|
|||
253 | archives=self.archivelist("tip")) |
|
|||
254 |
|
||||
255 | def search(self, query): |
|
|||
256 |
|
||||
257 | def changelist(**map): |
|
|||
258 | cl = self.repo.changelog |
|
|||
259 | count = 0 |
|
|||
260 | qw = query.lower().split() |
|
|||
261 |
|
||||
262 | def revgen(): |
|
|||
263 | for i in range(cl.count() - 1, 0, -100): |
|
|||
264 | l = [] |
|
|||
265 | for j in range(max(0, i - 100), i): |
|
|||
266 | n = cl.node(j) |
|
|||
267 | changes = cl.read(n) |
|
|||
268 | l.append((n, j, changes)) |
|
|||
269 | l.reverse() |
|
|||
270 | for e in l: |
|
|||
271 | yield e |
|
|||
272 |
|
||||
273 | for n, i, changes in revgen(): |
|
|||
274 | miss = 0 |
|
|||
275 | for q in qw: |
|
|||
276 | if not (q in changes[1].lower() or |
|
|||
277 | q in changes[4].lower() or |
|
|||
278 | q in " ".join(changes[3][:20]).lower()): |
|
|||
279 | miss = 1 |
|
|||
280 | break |
|
|||
281 | if miss: |
|
|||
282 | continue |
|
|||
283 |
|
||||
284 | count += 1 |
|
|||
285 | hn = hex(n) |
|
|||
286 |
|
||||
287 | yield self.t('searchentry', |
|
|||
288 | parity=count & 1, |
|
|||
289 | author=changes[1], |
|
|||
290 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
291 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
292 | changelogtag=self.showtag("changelogtag",n), |
|
|||
293 | manifest=hex(changes[0]), |
|
|||
294 | desc=changes[4], |
|
|||
295 | date=changes[2], |
|
|||
296 | files=self.listfilediffs(changes[3], n), |
|
|||
297 | rev=i, |
|
|||
298 | node=hn) |
|
|||
299 |
|
||||
300 | if count >= self.maxchanges: |
|
|||
301 | break |
|
|||
302 |
|
||||
303 | cl = self.repo.changelog |
|
|||
304 | mf = cl.read(cl.tip())[0] |
|
|||
305 |
|
||||
306 | yield self.t('search', |
|
|||
307 | query=query, |
|
|||
308 | manifest=hex(mf), |
|
|||
309 | entries=changelist) |
|
|||
310 |
|
||||
311 | def changeset(self, nodeid): |
|
|||
312 | cl = self.repo.changelog |
|
|||
313 | n = self.repo.lookup(nodeid) |
|
|||
314 | nodeid = hex(n) |
|
|||
315 | changes = cl.read(n) |
|
|||
316 | p1 = cl.parents(n)[0] |
|
|||
317 |
|
||||
318 | files = [] |
|
|||
319 | mf = self.repo.manifest.read(changes[0]) |
|
|||
320 | for f in changes[3]: |
|
|||
321 | files.append(self.t("filenodelink", |
|
|||
322 | filenode=hex(mf.get(f, nullid)), file=f)) |
|
|||
323 |
|
||||
324 | def diff(**map): |
|
|||
325 | yield self.diff(p1, n, None) |
|
|||
326 |
|
||||
327 | yield self.t('changeset', |
|
|||
328 | diff=diff, |
|
|||
329 | rev=cl.rev(n), |
|
|||
330 | node=nodeid, |
|
|||
331 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
332 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
333 | changesettag=self.showtag("changesettag",n), |
|
|||
334 | manifest=hex(changes[0]), |
|
|||
335 | author=changes[1], |
|
|||
336 | desc=changes[4], |
|
|||
337 | date=changes[2], |
|
|||
338 | files=files, |
|
|||
339 | archives=self.archivelist(nodeid)) |
|
|||
340 |
|
||||
341 | def filelog(self, f, filenode): |
|
|||
342 | cl = self.repo.changelog |
|
|||
343 | fl = self.repo.file(f) |
|
|||
344 | filenode = hex(fl.lookup(filenode)) |
|
|||
345 | count = fl.count() |
|
|||
346 |
|
||||
347 | def entries(**map): |
|
|||
348 | l = [] |
|
|||
349 | parity = (count - 1) & 1 |
|
|||
350 |
|
||||
351 | for i in range(count): |
|
|||
352 | n = fl.node(i) |
|
|||
353 | lr = fl.linkrev(n) |
|
|||
354 | cn = cl.node(lr) |
|
|||
355 | cs = cl.read(cl.node(lr)) |
|
|||
356 |
|
||||
357 | l.insert(0, {"parity": parity, |
|
|||
358 | "filenode": hex(n), |
|
|||
359 | "filerev": i, |
|
|||
360 | "file": f, |
|
|||
361 | "node": hex(cn), |
|
|||
362 | "author": cs[1], |
|
|||
363 | "date": cs[2], |
|
|||
364 | "rename": self.renamelink(fl, n), |
|
|||
365 | "parent": self.siblings(fl.parents(n), |
|
|||
366 | fl.rev, file=f), |
|
|||
367 | "child": self.siblings(fl.children(n), |
|
|||
368 | fl.rev, file=f), |
|
|||
369 | "desc": cs[4]}) |
|
|||
370 | parity = 1 - parity |
|
|||
371 |
|
||||
372 | for e in l: |
|
|||
373 | yield e |
|
|||
374 |
|
||||
375 | yield self.t("filelog", file=f, filenode=filenode, entries=entries) |
|
|||
376 |
|
||||
377 | def filerevision(self, f, node): |
|
|||
378 | fl = self.repo.file(f) |
|
|||
379 | n = fl.lookup(node) |
|
|||
380 | node = hex(n) |
|
|||
381 | text = fl.read(n) |
|
|||
382 | changerev = fl.linkrev(n) |
|
|||
383 | cl = self.repo.changelog |
|
|||
384 | cn = cl.node(changerev) |
|
|||
385 | cs = cl.read(cn) |
|
|||
386 | mfn = cs[0] |
|
|||
387 |
|
||||
388 | mt = mimetypes.guess_type(f)[0] |
|
|||
389 | rawtext = text |
|
|||
390 | if util.binary(text): |
|
|||
391 | mt = mt or 'application/octet-stream' |
|
|||
392 | text = "(binary:%s)" % mt |
|
|||
393 | mt = mt or 'text/plain' |
|
|||
394 |
|
||||
395 | def lines(): |
|
|||
396 | for l, t in enumerate(text.splitlines(1)): |
|
|||
397 | yield {"line": t, |
|
|||
398 | "linenumber": "% 6d" % (l + 1), |
|
|||
399 | "parity": l & 1} |
|
|||
400 |
|
||||
401 | yield self.t("filerevision", |
|
|||
402 | file=f, |
|
|||
403 | filenode=node, |
|
|||
404 | path=up(f), |
|
|||
405 | text=lines(), |
|
|||
406 | raw=rawtext, |
|
|||
407 | mimetype=mt, |
|
|||
408 | rev=changerev, |
|
|||
409 | node=hex(cn), |
|
|||
410 | manifest=hex(mfn), |
|
|||
411 | author=cs[1], |
|
|||
412 | date=cs[2], |
|
|||
413 | parent=self.siblings(fl.parents(n), fl.rev, file=f), |
|
|||
414 | child=self.siblings(fl.children(n), fl.rev, file=f), |
|
|||
415 | rename=self.renamelink(fl, n), |
|
|||
416 | permissions=self.repo.manifest.readflags(mfn)[f]) |
|
|||
417 |
|
||||
418 | def fileannotate(self, f, node): |
|
|||
419 | bcache = {} |
|
|||
420 | ncache = {} |
|
|||
421 | fl = self.repo.file(f) |
|
|||
422 | n = fl.lookup(node) |
|
|||
423 | node = hex(n) |
|
|||
424 | changerev = fl.linkrev(n) |
|
|||
425 |
|
||||
426 | cl = self.repo.changelog |
|
|||
427 | cn = cl.node(changerev) |
|
|||
428 | cs = cl.read(cn) |
|
|||
429 | mfn = cs[0] |
|
|||
430 |
|
||||
431 | def annotate(**map): |
|
|||
432 | parity = 1 |
|
|||
433 | last = None |
|
|||
434 | for r, l in fl.annotate(n): |
|
|||
435 | try: |
|
|||
436 | cnode = ncache[r] |
|
|||
437 | except KeyError: |
|
|||
438 | cnode = ncache[r] = self.repo.changelog.node(r) |
|
|||
439 |
|
||||
440 | try: |
|
|||
441 | name = bcache[r] |
|
|||
442 | except KeyError: |
|
|||
443 | cl = self.repo.changelog.read(cnode) |
|
|||
444 | bcache[r] = name = self.repo.ui.shortuser(cl[1]) |
|
|||
445 |
|
||||
446 | if last != cnode: |
|
|||
447 | parity = 1 - parity |
|
|||
448 | last = cnode |
|
|||
449 |
|
||||
450 | yield {"parity": parity, |
|
|||
451 | "node": hex(cnode), |
|
|||
452 | "rev": r, |
|
|||
453 | "author": name, |
|
|||
454 | "file": f, |
|
|||
455 | "line": l} |
|
|||
456 |
|
||||
457 | yield self.t("fileannotate", |
|
|||
458 | file=f, |
|
|||
459 | filenode=node, |
|
|||
460 | annotate=annotate, |
|
|||
461 | path=up(f), |
|
|||
462 | rev=changerev, |
|
|||
463 | node=hex(cn), |
|
|||
464 | manifest=hex(mfn), |
|
|||
465 | author=cs[1], |
|
|||
466 | date=cs[2], |
|
|||
467 | rename=self.renamelink(fl, n), |
|
|||
468 | parent=self.siblings(fl.parents(n), fl.rev, file=f), |
|
|||
469 | child=self.siblings(fl.children(n), fl.rev, file=f), |
|
|||
470 | permissions=self.repo.manifest.readflags(mfn)[f]) |
|
|||
471 |
|
||||
472 | def manifest(self, mnode, path): |
|
|||
473 | man = self.repo.manifest |
|
|||
474 | mn = man.lookup(mnode) |
|
|||
475 | mnode = hex(mn) |
|
|||
476 | mf = man.read(mn) |
|
|||
477 | rev = man.rev(mn) |
|
|||
478 | changerev = man.linkrev(mn) |
|
|||
479 | node = self.repo.changelog.node(changerev) |
|
|||
480 | mff = man.readflags(mn) |
|
|||
481 |
|
||||
482 | files = {} |
|
|||
483 |
|
||||
484 | p = path[1:] |
|
|||
485 | if p and p[-1] != "/": |
|
|||
486 | p += "/" |
|
|||
487 | l = len(p) |
|
|||
488 |
|
||||
489 | for f,n in mf.items(): |
|
|||
490 | if f[:l] != p: |
|
|||
491 | continue |
|
|||
492 | remain = f[l:] |
|
|||
493 | if "/" in remain: |
|
|||
494 | short = remain[:remain.find("/") + 1] # bleah |
|
|||
495 | files[short] = (f, None) |
|
|||
496 | else: |
|
|||
497 | short = os.path.basename(remain) |
|
|||
498 | files[short] = (f, n) |
|
|||
499 |
|
||||
500 | def filelist(**map): |
|
|||
501 | parity = 0 |
|
|||
502 | fl = files.keys() |
|
|||
503 | fl.sort() |
|
|||
504 | for f in fl: |
|
|||
505 | full, fnode = files[f] |
|
|||
506 | if not fnode: |
|
|||
507 | continue |
|
|||
508 |
|
||||
509 | yield {"file": full, |
|
|||
510 | "manifest": mnode, |
|
|||
511 | "filenode": hex(fnode), |
|
|||
512 | "parity": parity, |
|
|||
513 | "basename": f, |
|
|||
514 | "permissions": mff[full]} |
|
|||
515 | parity = 1 - parity |
|
|||
516 |
|
||||
517 | def dirlist(**map): |
|
|||
518 | parity = 0 |
|
|||
519 | fl = files.keys() |
|
|||
520 | fl.sort() |
|
|||
521 | for f in fl: |
|
|||
522 | full, fnode = files[f] |
|
|||
523 | if fnode: |
|
|||
524 | continue |
|
|||
525 |
|
||||
526 | yield {"parity": parity, |
|
|||
527 | "path": os.path.join(path, f), |
|
|||
528 | "manifest": mnode, |
|
|||
529 | "basename": f[:-1]} |
|
|||
530 | parity = 1 - parity |
|
|||
531 |
|
||||
532 | yield self.t("manifest", |
|
|||
533 | manifest=mnode, |
|
|||
534 | rev=rev, |
|
|||
535 | node=hex(node), |
|
|||
536 | path=path, |
|
|||
537 | up=up(path), |
|
|||
538 | fentries=filelist, |
|
|||
539 | dentries=dirlist, |
|
|||
540 | archives=self.archivelist(hex(node))) |
|
|||
541 |
|
||||
542 | def tags(self): |
|
|||
543 | cl = self.repo.changelog |
|
|||
544 | mf = cl.read(cl.tip())[0] |
|
|||
545 |
|
||||
546 | i = self.repo.tagslist() |
|
|||
547 | i.reverse() |
|
|||
548 |
|
||||
549 | def entries(notip=False, **map): |
|
|||
550 | parity = 0 |
|
|||
551 | for k,n in i: |
|
|||
552 | if notip and k == "tip": continue |
|
|||
553 | yield {"parity": parity, |
|
|||
554 | "tag": k, |
|
|||
555 | "tagmanifest": hex(cl.read(n)[0]), |
|
|||
556 | "date": cl.read(n)[2], |
|
|||
557 | "node": hex(n)} |
|
|||
558 | parity = 1 - parity |
|
|||
559 |
|
||||
560 | yield self.t("tags", |
|
|||
561 | manifest=hex(mf), |
|
|||
562 | entries=lambda **x: entries(False, **x), |
|
|||
563 | entriesnotip=lambda **x: entries(True, **x)) |
|
|||
564 |
|
||||
565 | def summary(self): |
|
|||
566 | cl = self.repo.changelog |
|
|||
567 | mf = cl.read(cl.tip())[0] |
|
|||
568 |
|
||||
569 | i = self.repo.tagslist() |
|
|||
570 | i.reverse() |
|
|||
571 |
|
||||
572 | def tagentries(**map): |
|
|||
573 | parity = 0 |
|
|||
574 | count = 0 |
|
|||
575 | for k,n in i: |
|
|||
576 | if k == "tip": # skip tip |
|
|||
577 | continue; |
|
|||
578 |
|
||||
579 | count += 1 |
|
|||
580 | if count > 10: # limit to 10 tags |
|
|||
581 | break; |
|
|||
582 |
|
||||
583 | c = cl.read(n) |
|
|||
584 | m = c[0] |
|
|||
585 | t = c[2] |
|
|||
586 |
|
||||
587 | yield self.t("tagentry", |
|
|||
588 | parity = parity, |
|
|||
589 | tag = k, |
|
|||
590 | node = hex(n), |
|
|||
591 | date = t, |
|
|||
592 | tagmanifest = hex(m)) |
|
|||
593 | parity = 1 - parity |
|
|||
594 |
|
||||
595 | def changelist(**map): |
|
|||
596 | parity = 0 |
|
|||
597 | cl = self.repo.changelog |
|
|||
598 | l = [] # build a list in forward order for efficiency |
|
|||
599 | for i in range(start, end): |
|
|||
600 | n = cl.node(i) |
|
|||
601 | changes = cl.read(n) |
|
|||
602 | hn = hex(n) |
|
|||
603 | t = changes[2] |
|
|||
604 |
|
||||
605 | l.insert(0, self.t( |
|
|||
606 | 'shortlogentry', |
|
|||
607 | parity = parity, |
|
|||
608 | author = changes[1], |
|
|||
609 | manifest = hex(changes[0]), |
|
|||
610 | desc = changes[4], |
|
|||
611 | date = t, |
|
|||
612 | rev = i, |
|
|||
613 | node = hn)) |
|
|||
614 | parity = 1 - parity |
|
|||
615 |
|
||||
616 | yield l |
|
|||
617 |
|
||||
618 | cl = self.repo.changelog |
|
|||
619 | mf = cl.read(cl.tip())[0] |
|
|||
620 | count = cl.count() |
|
|||
621 | start = max(0, count - self.maxchanges) |
|
|||
622 | end = min(count, start + self.maxchanges) |
|
|||
623 | pos = end - 1 |
|
|||
624 |
|
||||
625 | yield self.t("summary", |
|
|||
626 | desc = self.repo.ui.config("web", "description", "unknown"), |
|
|||
627 | owner = (self.repo.ui.config("ui", "username") or # preferred |
|
|||
628 | self.repo.ui.config("web", "contact") or # deprecated |
|
|||
629 | self.repo.ui.config("web", "author", "unknown")), # also |
|
|||
630 | lastchange = (0, 0), # FIXME |
|
|||
631 | manifest = hex(mf), |
|
|||
632 | tags = tagentries, |
|
|||
633 | shortlog = changelist) |
|
|||
634 |
|
||||
635 | def filediff(self, file, changeset): |
|
|||
636 | cl = self.repo.changelog |
|
|||
637 | n = self.repo.lookup(changeset) |
|
|||
638 | changeset = hex(n) |
|
|||
639 | p1 = cl.parents(n)[0] |
|
|||
640 | cs = cl.read(n) |
|
|||
641 | mf = self.repo.manifest.read(cs[0]) |
|
|||
642 |
|
||||
643 | def diff(**map): |
|
|||
644 | yield self.diff(p1, n, [file]) |
|
|||
645 |
|
||||
646 | yield self.t("filediff", |
|
|||
647 | file=file, |
|
|||
648 | filenode=hex(mf.get(file, nullid)), |
|
|||
649 | node=changeset, |
|
|||
650 | rev=self.repo.changelog.rev(n), |
|
|||
651 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
652 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
653 | diff=diff) |
|
|||
654 |
|
||||
655 | archive_specs = { |
|
|||
656 | 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', 'x-bzip2'), |
|
|||
657 | 'gz': ('application/x-tar', 'tgz', '.tar.gz', 'x-gzip'), |
|
|||
658 | 'zip': ('application/zip', 'zip', '.zip', None), |
|
|||
659 | } |
|
|||
660 |
|
||||
661 | def archive(self, req, cnode, type): |
|
|||
662 | reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame)) |
|
|||
663 | name = "%s-%s" % (reponame, short(cnode)) |
|
|||
664 | mimetype, artype, extension, encoding = self.archive_specs[type] |
|
|||
665 | headers = [('Content-type', mimetype), |
|
|||
666 | ('Content-disposition', 'attachment; filename=%s%s' % |
|
|||
667 | (name, extension))] |
|
|||
668 | if encoding: |
|
|||
669 | headers.append(('Content-encoding', encoding)) |
|
|||
670 | req.header(headers) |
|
|||
671 | archival.archive(self.repo, req.out, cnode, artype, prefix=name) |
|
|||
672 |
|
||||
673 | # add tags to things |
|
|||
674 | # tags -> list of changesets corresponding to tags |
|
|||
675 | # find tag, changeset, file |
|
|||
676 |
|
||||
677 | def run(self, req=hgrequest()): |
|
|||
678 | def clean(path): |
|
|||
679 | p = util.normpath(path) |
|
|||
680 | if p[:2] == "..": |
|
|||
681 | raise "suspicious path" |
|
|||
682 | return p |
|
|||
683 |
|
||||
684 | def header(**map): |
|
|||
685 | yield self.t("header", **map) |
|
|||
686 |
|
||||
687 | def footer(**map): |
|
|||
688 | yield self.t("footer", |
|
|||
689 | motd=self.repo.ui.config("web", "motd", ""), |
|
|||
690 | **map) |
|
|||
691 |
|
||||
692 | def expand_form(form): |
|
|||
693 | shortcuts = { |
|
|||
694 | 'cl': [('cmd', ['changelog']), ('rev', None)], |
|
|||
695 | 'cs': [('cmd', ['changeset']), ('node', None)], |
|
|||
696 | 'f': [('cmd', ['file']), ('filenode', None)], |
|
|||
697 | 'fl': [('cmd', ['filelog']), ('filenode', None)], |
|
|||
698 | 'fd': [('cmd', ['filediff']), ('node', None)], |
|
|||
699 | 'fa': [('cmd', ['annotate']), ('filenode', None)], |
|
|||
700 | 'mf': [('cmd', ['manifest']), ('manifest', None)], |
|
|||
701 | 'ca': [('cmd', ['archive']), ('node', None)], |
|
|||
702 | 'tags': [('cmd', ['tags'])], |
|
|||
703 | 'tip': [('cmd', ['changeset']), ('node', ['tip'])], |
|
|||
704 | 'static': [('cmd', ['static']), ('file', None)] |
|
|||
705 | } |
|
|||
706 |
|
||||
707 | for k in shortcuts.iterkeys(): |
|
|||
708 | if form.has_key(k): |
|
|||
709 | for name, value in shortcuts[k]: |
|
|||
710 | if value is None: |
|
|||
711 | value = form[k] |
|
|||
712 | form[name] = value |
|
|||
713 | del form[k] |
|
|||
714 |
|
||||
715 | self.refresh() |
|
|||
716 |
|
||||
717 | expand_form(req.form) |
|
|||
718 |
|
||||
719 | t = self.repo.ui.config("web", "templates", templater.templatepath()) |
|
|||
720 | static = self.repo.ui.config("web", "static", os.path.join(t,"static")) |
|
|||
721 | m = os.path.join(t, "map") |
|
|||
722 | style = self.repo.ui.config("web", "style", "") |
|
|||
723 | if req.form.has_key('style'): |
|
|||
724 | style = req.form['style'][0] |
|
|||
725 | if style: |
|
|||
726 | b = os.path.basename("map-" + style) |
|
|||
727 | p = os.path.join(t, b) |
|
|||
728 | if os.path.isfile(p): |
|
|||
729 | m = p |
|
|||
730 |
|
||||
731 | port = req.env["SERVER_PORT"] |
|
|||
732 | port = port != "80" and (":" + port) or "" |
|
|||
733 | uri = req.env["REQUEST_URI"] |
|
|||
734 | if "?" in uri: |
|
|||
735 | uri = uri.split("?")[0] |
|
|||
736 | url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri) |
|
|||
737 | if not self.reponame: |
|
|||
738 | self.reponame = (self.repo.ui.config("web", "name") |
|
|||
739 | or uri.strip('/') or self.repo.root) |
|
|||
740 |
|
||||
741 | self.t = templater.templater(m, templater.common_filters, |
|
|||
742 | defaults={"url": url, |
|
|||
743 | "repo": self.reponame, |
|
|||
744 | "header": header, |
|
|||
745 | "footer": footer, |
|
|||
746 | }) |
|
|||
747 |
|
||||
748 | if not req.form.has_key('cmd'): |
|
|||
749 | req.form['cmd'] = [self.t.cache['default'],] |
|
|||
750 |
|
||||
751 | cmd = req.form['cmd'][0] |
|
|||
752 | if cmd == 'changelog': |
|
|||
753 | hi = self.repo.changelog.count() - 1 |
|
|||
754 | if req.form.has_key('rev'): |
|
|||
755 | hi = req.form['rev'][0] |
|
|||
756 | try: |
|
|||
757 | hi = self.repo.changelog.rev(self.repo.lookup(hi)) |
|
|||
758 | except hg.RepoError: |
|
|||
759 | req.write(self.search(hi)) # XXX redirect to 404 page? |
|
|||
760 | return |
|
|||
761 |
|
||||
762 | req.write(self.changelog(hi)) |
|
|||
763 |
|
||||
764 | elif cmd == 'changeset': |
|
|||
765 | req.write(self.changeset(req.form['node'][0])) |
|
|||
766 |
|
||||
767 | elif cmd == 'manifest': |
|
|||
768 | req.write(self.manifest(req.form['manifest'][0], |
|
|||
769 | clean(req.form['path'][0]))) |
|
|||
770 |
|
||||
771 | elif cmd == 'tags': |
|
|||
772 | req.write(self.tags()) |
|
|||
773 |
|
||||
774 | elif cmd == 'summary': |
|
|||
775 | req.write(self.summary()) |
|
|||
776 |
|
||||
777 | elif cmd == 'filediff': |
|
|||
778 | req.write(self.filediff(clean(req.form['file'][0]), |
|
|||
779 | req.form['node'][0])) |
|
|||
780 |
|
||||
781 | elif cmd == 'file': |
|
|||
782 | req.write(self.filerevision(clean(req.form['file'][0]), |
|
|||
783 | req.form['filenode'][0])) |
|
|||
784 |
|
||||
785 | elif cmd == 'annotate': |
|
|||
786 | req.write(self.fileannotate(clean(req.form['file'][0]), |
|
|||
787 | req.form['filenode'][0])) |
|
|||
788 |
|
||||
789 | elif cmd == 'filelog': |
|
|||
790 | req.write(self.filelog(clean(req.form['file'][0]), |
|
|||
791 | req.form['filenode'][0])) |
|
|||
792 |
|
||||
793 | elif cmd == 'heads': |
|
|||
794 | req.httphdr("application/mercurial-0.1") |
|
|||
795 | h = self.repo.heads() |
|
|||
796 | req.write(" ".join(map(hex, h)) + "\n") |
|
|||
797 |
|
||||
798 | elif cmd == 'branches': |
|
|||
799 | req.httphdr("application/mercurial-0.1") |
|
|||
800 | nodes = [] |
|
|||
801 | if req.form.has_key('nodes'): |
|
|||
802 | nodes = map(bin, req.form['nodes'][0].split(" ")) |
|
|||
803 | for b in self.repo.branches(nodes): |
|
|||
804 | req.write(" ".join(map(hex, b)) + "\n") |
|
|||
805 |
|
||||
806 | elif cmd == 'between': |
|
|||
807 | req.httphdr("application/mercurial-0.1") |
|
|||
808 | nodes = [] |
|
|||
809 | if req.form.has_key('pairs'): |
|
|||
810 | pairs = [map(bin, p.split("-")) |
|
|||
811 | for p in req.form['pairs'][0].split(" ")] |
|
|||
812 | for b in self.repo.between(pairs): |
|
|||
813 | req.write(" ".join(map(hex, b)) + "\n") |
|
|||
814 |
|
||||
815 | elif cmd == 'changegroup': |
|
|||
816 | req.httphdr("application/mercurial-0.1") |
|
|||
817 | nodes = [] |
|
|||
818 | if not self.allowpull: |
|
|||
819 | return |
|
|||
820 |
|
||||
821 | if req.form.has_key('roots'): |
|
|||
822 | nodes = map(bin, req.form['roots'][0].split(" ")) |
|
|||
823 |
|
||||
824 | z = zlib.compressobj() |
|
|||
825 | f = self.repo.changegroup(nodes, 'serve') |
|
|||
826 | while 1: |
|
|||
827 | chunk = f.read(4096) |
|
|||
828 | if not chunk: |
|
|||
829 | break |
|
|||
830 | req.write(z.compress(chunk)) |
|
|||
831 |
|
||||
832 | req.write(z.flush()) |
|
|||
833 |
|
||||
834 | elif cmd == 'archive': |
|
|||
835 | changeset = self.repo.lookup(req.form['node'][0]) |
|
|||
836 | type = req.form['type'][0] |
|
|||
837 | if (type in self.archives and |
|
|||
838 | self.repo.ui.configbool("web", "allow" + type, False)): |
|
|||
839 | self.archive(req, changeset, type) |
|
|||
840 | return |
|
|||
841 |
|
||||
842 | req.write(self.t("error")) |
|
|||
843 |
|
||||
844 | elif cmd == 'static': |
|
|||
845 | fname = req.form['file'][0] |
|
|||
846 | req.write(staticfile(static, fname) |
|
|||
847 | or self.t("error", error="%r not found" % fname)) |
|
|||
848 |
|
||||
849 | else: |
|
|||
850 | req.write(self.t("error")) |
|
|||
851 |
|
||||
852 | # This is a stopgap |
|
|||
853 | class hgwebdir(object): |
|
|||
854 | def __init__(self, config): |
|
|||
855 | def cleannames(items): |
|
|||
856 | return [(name.strip(os.sep), path) for name, path in items] |
|
|||
857 |
|
||||
858 | self.motd = "" |
|
|||
859 | self.repos_sorted = ('name', False) |
|
|||
860 | if isinstance(config, (list, tuple)): |
|
|||
861 | self.repos = cleannames(config) |
|
|||
862 | self.repos_sorted = ('', False) |
|
|||
863 | elif isinstance(config, dict): |
|
|||
864 | self.repos = cleannames(config.items()) |
|
|||
865 | self.repos.sort() |
|
|||
866 | else: |
|
|||
867 | cp = ConfigParser.SafeConfigParser() |
|
|||
868 | cp.read(config) |
|
|||
869 | self.repos = [] |
|
|||
870 | if cp.has_section('web') and cp.has_option('web', 'motd'): |
|
|||
871 | self.motd = cp.get('web', 'motd') |
|
|||
872 | if cp.has_section('paths'): |
|
|||
873 | self.repos.extend(cleannames(cp.items('paths'))) |
|
|||
874 | if cp.has_section('collections'): |
|
|||
875 | for prefix, root in cp.items('collections'): |
|
|||
876 | for path in util.walkrepos(root): |
|
|||
877 | repo = os.path.normpath(path) |
|
|||
878 | name = repo |
|
|||
879 | if name.startswith(prefix): |
|
|||
880 | name = name[len(prefix):] |
|
|||
881 | self.repos.append((name.lstrip(os.sep), repo)) |
|
|||
882 | self.repos.sort() |
|
|||
883 |
|
||||
884 | def run(self, req=hgrequest()): |
|
|||
885 | def header(**map): |
|
|||
886 | yield tmpl("header", **map) |
|
|||
887 |
|
||||
888 | def footer(**map): |
|
|||
889 | yield tmpl("footer", motd=self.motd, **map) |
|
|||
890 |
|
||||
891 | m = os.path.join(templater.templatepath(), "map") |
|
|||
892 | tmpl = templater.templater(m, templater.common_filters, |
|
|||
893 | defaults={"header": header, |
|
|||
894 | "footer": footer}) |
|
|||
895 |
|
||||
896 | def archivelist(ui, nodeid, url): |
|
|||
897 | for i in ['zip', 'gz', 'bz2']: |
|
|||
898 | if ui.configbool("web", "allow" + i, False): |
|
|||
899 | yield {"type" : i, "node": nodeid, "url": url} |
|
|||
900 |
|
||||
901 | def entries(sortcolumn="", descending=False, **map): |
|
|||
902 | rows = [] |
|
|||
903 | parity = 0 |
|
|||
904 | for name, path in self.repos: |
|
|||
905 | u = ui.ui() |
|
|||
906 | try: |
|
|||
907 | u.readconfig(os.path.join(path, '.hg', 'hgrc')) |
|
|||
908 | except IOError: |
|
|||
909 | pass |
|
|||
910 | get = u.config |
|
|||
911 |
|
||||
912 | url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name]) |
|
|||
913 | .replace("//", "/")) |
|
|||
914 |
|
||||
915 | # update time with local timezone |
|
|||
916 | try: |
|
|||
917 | d = (get_mtime(path), util.makedate()[1]) |
|
|||
918 | except OSError: |
|
|||
919 | continue |
|
|||
920 |
|
||||
921 | contact = (get("ui", "username") or # preferred |
|
|||
922 | get("web", "contact") or # deprecated |
|
|||
923 | get("web", "author", "")) # also |
|
|||
924 | description = get("web", "description", "") |
|
|||
925 | name = get("web", "name", name) |
|
|||
926 | row = dict(contact=contact or "unknown", |
|
|||
927 | contact_sort=contact.upper() or "unknown", |
|
|||
928 | name=name, |
|
|||
929 | name_sort=name, |
|
|||
930 | url=url, |
|
|||
931 | description=description or "unknown", |
|
|||
932 | description_sort=description.upper() or "unknown", |
|
|||
933 | lastchange=d, |
|
|||
934 | lastchange_sort=d[1]-d[0], |
|
|||
935 | archives=archivelist(u, "tip", url)) |
|
|||
936 | if (not sortcolumn |
|
|||
937 | or (sortcolumn, descending) == self.repos_sorted): |
|
|||
938 | # fast path for unsorted output |
|
|||
939 | row['parity'] = parity |
|
|||
940 | parity = 1 - parity |
|
|||
941 | yield row |
|
|||
942 | else: |
|
|||
943 | rows.append((row["%s_sort" % sortcolumn], row)) |
|
|||
944 | if rows: |
|
|||
945 | rows.sort() |
|
|||
946 | if descending: |
|
|||
947 | rows.reverse() |
|
|||
948 | for key, row in rows: |
|
|||
949 | row['parity'] = parity |
|
|||
950 | parity = 1 - parity |
|
|||
951 | yield row |
|
|||
952 |
|
||||
953 | virtual = req.env.get("PATH_INFO", "").strip('/') |
|
|||
954 | if virtual: |
|
|||
955 | real = dict(self.repos).get(virtual) |
|
|||
956 | if real: |
|
|||
957 | try: |
|
|||
958 | hgweb(real).run(req) |
|
|||
959 | except IOError, inst: |
|
|||
960 | req.write(tmpl("error", error=inst.strerror)) |
|
|||
961 | except hg.RepoError, inst: |
|
|||
962 | req.write(tmpl("error", error=str(inst))) |
|
|||
963 | else: |
|
|||
964 | req.write(tmpl("notfound", repo=virtual)) |
|
|||
965 | else: |
|
|||
966 | if req.form.has_key('static'): |
|
|||
967 | static = os.path.join(templater.templatepath(), "static") |
|
|||
968 | fname = req.form['static'][0] |
|
|||
969 | req.write(staticfile(static, fname) |
|
|||
970 | or tmpl("error", error="%r not found" % fname)) |
|
|||
971 | else: |
|
|||
972 | sortable = ["name", "description", "contact", "lastchange"] |
|
|||
973 | sortcolumn, descending = self.repos_sorted |
|
|||
974 | if req.form.has_key('sort'): |
|
|||
975 | sortcolumn = req.form['sort'][0] |
|
|||
976 | descending = sortcolumn.startswith('-') |
|
|||
977 | if descending: |
|
|||
978 | sortcolumn = sortcolumn[1:] |
|
|||
979 | if sortcolumn not in sortable: |
|
|||
980 | sortcolumn = "" |
|
|||
981 |
|
||||
982 | sort = [("sort_%s" % column, |
|
|||
983 | "%s%s" % ((not descending and column == sortcolumn) |
|
|||
984 | and "-" or "", column)) |
|
|||
985 | for column in sortable] |
|
|||
986 | req.write(tmpl("index", entries=entries, |
|
|||
987 | sortcolumn=sortcolumn, descending=descending, |
|
|||
988 | **dict(sort))) |
|
This diff has been collapsed as it changes many lines, (950 lines changed) Show them Hide them | |||||
@@ -6,25 +6,8 b'' | |||||
6 | # This software may be used and distributed according to the terms |
|
6 | # This software may be used and distributed according to the terms | |
7 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | # of the GNU General Public License, incorporated herein by reference. | |
8 |
|
8 | |||
9 |
import os, |
|
9 | import os, mimetypes | |
10 | import mimetypes |
|
10 | import os.path | |
11 | from mercurial.demandload import demandload |
|
|||
12 | demandload(globals(), "time re socket zlib errno ConfigParser tempfile") |
|
|||
13 | demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater") |
|
|||
14 | demandload(globals(), "mercurial.hgweb.request:hgrequest") |
|
|||
15 | demandload(globals(), "mercurial.hgweb.server:create_server") |
|
|||
16 | from mercurial.node import * |
|
|||
17 | from mercurial.i18n import gettext as _ |
|
|||
18 |
|
||||
19 | def up(p): |
|
|||
20 | if p[0] != "/": |
|
|||
21 | p = "/" + p |
|
|||
22 | if p[-1] == "/": |
|
|||
23 | p = p[:-1] |
|
|||
24 | up = os.path.dirname(p) |
|
|||
25 | if up == "/": |
|
|||
26 | return "/" |
|
|||
27 | return up + "/" |
|
|||
28 |
|
11 | |||
29 | def get_mtime(repo_path): |
|
12 | def get_mtime(repo_path): | |
30 | hg_path = os.path.join(repo_path, ".hg") |
|
13 | hg_path = os.path.join(repo_path, ".hg") | |
@@ -57,932 +40,3 b' def staticfile(directory, fname):' | |||||
57 | except (TypeError, OSError): |
|
40 | except (TypeError, OSError): | |
58 | # illegal fname or unreadable file |
|
41 | # illegal fname or unreadable file | |
59 | return "" |
|
42 | return "" | |
60 |
|
||||
61 | class hgweb(object): |
|
|||
62 | def __init__(self, repo, name=None): |
|
|||
63 | if type(repo) == type(""): |
|
|||
64 | self.repo = hg.repository(ui.ui(), repo) |
|
|||
65 | else: |
|
|||
66 | self.repo = repo |
|
|||
67 |
|
||||
68 | self.mtime = -1 |
|
|||
69 | self.reponame = name |
|
|||
70 | self.archives = 'zip', 'gz', 'bz2' |
|
|||
71 |
|
||||
72 | def refresh(self): |
|
|||
73 | mtime = get_mtime(self.repo.root) |
|
|||
74 | if mtime != self.mtime: |
|
|||
75 | self.mtime = mtime |
|
|||
76 | self.repo = hg.repository(self.repo.ui, self.repo.root) |
|
|||
77 | self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10)) |
|
|||
78 | self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10)) |
|
|||
79 | self.allowpull = self.repo.ui.configbool("web", "allowpull", True) |
|
|||
80 |
|
||||
81 | def archivelist(self, nodeid): |
|
|||
82 | for i in self.archives: |
|
|||
83 | if self.repo.ui.configbool("web", "allow" + i, False): |
|
|||
84 | yield {"type" : i, "node" : nodeid, "url": ""} |
|
|||
85 |
|
||||
86 | def listfiles(self, files, mf): |
|
|||
87 | for f in files[:self.maxfiles]: |
|
|||
88 | yield self.t("filenodelink", node=hex(mf[f]), file=f) |
|
|||
89 | if len(files) > self.maxfiles: |
|
|||
90 | yield self.t("fileellipses") |
|
|||
91 |
|
||||
92 | def listfilediffs(self, files, changeset): |
|
|||
93 | for f in files[:self.maxfiles]: |
|
|||
94 | yield self.t("filedifflink", node=hex(changeset), file=f) |
|
|||
95 | if len(files) > self.maxfiles: |
|
|||
96 | yield self.t("fileellipses") |
|
|||
97 |
|
||||
98 | def siblings(self, siblings=[], rev=None, hiderev=None, **args): |
|
|||
99 | if not rev: |
|
|||
100 | rev = lambda x: "" |
|
|||
101 | siblings = [s for s in siblings if s != nullid] |
|
|||
102 | if len(siblings) == 1 and rev(siblings[0]) == hiderev: |
|
|||
103 | return |
|
|||
104 | for s in siblings: |
|
|||
105 | yield dict(node=hex(s), rev=rev(s), **args) |
|
|||
106 |
|
||||
107 | def renamelink(self, fl, node): |
|
|||
108 | r = fl.renamed(node) |
|
|||
109 | if r: |
|
|||
110 | return [dict(file=r[0], node=hex(r[1]))] |
|
|||
111 | return [] |
|
|||
112 |
|
||||
113 | def showtag(self, t1, node=nullid, **args): |
|
|||
114 | for t in self.repo.nodetags(node): |
|
|||
115 | yield self.t(t1, tag=t, **args) |
|
|||
116 |
|
||||
117 | def diff(self, node1, node2, files): |
|
|||
118 | def filterfiles(filters, files): |
|
|||
119 | l = [x for x in files if x in filters] |
|
|||
120 |
|
||||
121 | for t in filters: |
|
|||
122 | if t and t[-1] != os.sep: |
|
|||
123 | t += os.sep |
|
|||
124 | l += [x for x in files if x.startswith(t)] |
|
|||
125 | return l |
|
|||
126 |
|
||||
127 | parity = [0] |
|
|||
128 | def diffblock(diff, f, fn): |
|
|||
129 | yield self.t("diffblock", |
|
|||
130 | lines=prettyprintlines(diff), |
|
|||
131 | parity=parity[0], |
|
|||
132 | file=f, |
|
|||
133 | filenode=hex(fn or nullid)) |
|
|||
134 | parity[0] = 1 - parity[0] |
|
|||
135 |
|
||||
136 | def prettyprintlines(diff): |
|
|||
137 | for l in diff.splitlines(1): |
|
|||
138 | if l.startswith('+'): |
|
|||
139 | yield self.t("difflineplus", line=l) |
|
|||
140 | elif l.startswith('-'): |
|
|||
141 | yield self.t("difflineminus", line=l) |
|
|||
142 | elif l.startswith('@'): |
|
|||
143 | yield self.t("difflineat", line=l) |
|
|||
144 | else: |
|
|||
145 | yield self.t("diffline", line=l) |
|
|||
146 |
|
||||
147 | r = self.repo |
|
|||
148 | cl = r.changelog |
|
|||
149 | mf = r.manifest |
|
|||
150 | change1 = cl.read(node1) |
|
|||
151 | change2 = cl.read(node2) |
|
|||
152 | mmap1 = mf.read(change1[0]) |
|
|||
153 | mmap2 = mf.read(change2[0]) |
|
|||
154 | date1 = util.datestr(change1[2]) |
|
|||
155 | date2 = util.datestr(change2[2]) |
|
|||
156 |
|
||||
157 | modified, added, removed, deleted, unknown = r.changes(node1, node2) |
|
|||
158 | if files: |
|
|||
159 | modified, added, removed = map(lambda x: filterfiles(files, x), |
|
|||
160 | (modified, added, removed)) |
|
|||
161 |
|
||||
162 | diffopts = self.repo.ui.diffopts() |
|
|||
163 | showfunc = diffopts['showfunc'] |
|
|||
164 | ignorews = diffopts['ignorews'] |
|
|||
165 | for f in modified: |
|
|||
166 | to = r.file(f).read(mmap1[f]) |
|
|||
167 | tn = r.file(f).read(mmap2[f]) |
|
|||
168 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
169 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
170 | for f in added: |
|
|||
171 | to = None |
|
|||
172 | tn = r.file(f).read(mmap2[f]) |
|
|||
173 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
174 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
175 | for f in removed: |
|
|||
176 | to = r.file(f).read(mmap1[f]) |
|
|||
177 | tn = None |
|
|||
178 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
179 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
180 |
|
||||
181 | def changelog(self, pos): |
|
|||
182 | def changenav(**map): |
|
|||
183 | def seq(factor, maxchanges=None): |
|
|||
184 | if maxchanges: |
|
|||
185 | yield maxchanges |
|
|||
186 | if maxchanges >= 20 and maxchanges <= 40: |
|
|||
187 | yield 50 |
|
|||
188 | else: |
|
|||
189 | yield 1 * factor |
|
|||
190 | yield 3 * factor |
|
|||
191 | for f in seq(factor * 10): |
|
|||
192 | yield f |
|
|||
193 |
|
||||
194 | l = [] |
|
|||
195 | last = 0 |
|
|||
196 | for f in seq(1, self.maxchanges): |
|
|||
197 | if f < self.maxchanges or f <= last: |
|
|||
198 | continue |
|
|||
199 | if f > count: |
|
|||
200 | break |
|
|||
201 | last = f |
|
|||
202 | r = "%d" % f |
|
|||
203 | if pos + f < count: |
|
|||
204 | l.append(("+" + r, pos + f)) |
|
|||
205 | if pos - f >= 0: |
|
|||
206 | l.insert(0, ("-" + r, pos - f)) |
|
|||
207 |
|
||||
208 | yield {"rev": 0, "label": "(0)"} |
|
|||
209 |
|
||||
210 | for label, rev in l: |
|
|||
211 | yield {"label": label, "rev": rev} |
|
|||
212 |
|
||||
213 | yield {"label": "tip", "rev": "tip"} |
|
|||
214 |
|
||||
215 | def changelist(**map): |
|
|||
216 | parity = (start - end) & 1 |
|
|||
217 | cl = self.repo.changelog |
|
|||
218 | l = [] # build a list in forward order for efficiency |
|
|||
219 | for i in range(start, end): |
|
|||
220 | n = cl.node(i) |
|
|||
221 | changes = cl.read(n) |
|
|||
222 | hn = hex(n) |
|
|||
223 |
|
||||
224 | l.insert(0, {"parity": parity, |
|
|||
225 | "author": changes[1], |
|
|||
226 | "parent": self.siblings(cl.parents(n), cl.rev, |
|
|||
227 | cl.rev(n) - 1), |
|
|||
228 | "child": self.siblings(cl.children(n), cl.rev, |
|
|||
229 | cl.rev(n) + 1), |
|
|||
230 | "changelogtag": self.showtag("changelogtag",n), |
|
|||
231 | "manifest": hex(changes[0]), |
|
|||
232 | "desc": changes[4], |
|
|||
233 | "date": changes[2], |
|
|||
234 | "files": self.listfilediffs(changes[3], n), |
|
|||
235 | "rev": i, |
|
|||
236 | "node": hn}) |
|
|||
237 | parity = 1 - parity |
|
|||
238 |
|
||||
239 | for e in l: |
|
|||
240 | yield e |
|
|||
241 |
|
||||
242 | cl = self.repo.changelog |
|
|||
243 | mf = cl.read(cl.tip())[0] |
|
|||
244 | count = cl.count() |
|
|||
245 | start = max(0, pos - self.maxchanges + 1) |
|
|||
246 | end = min(count, start + self.maxchanges) |
|
|||
247 | pos = end - 1 |
|
|||
248 |
|
||||
249 | yield self.t('changelog', |
|
|||
250 | changenav=changenav, |
|
|||
251 | manifest=hex(mf), |
|
|||
252 | rev=pos, changesets=count, entries=changelist, |
|
|||
253 | archives=self.archivelist("tip")) |
|
|||
254 |
|
||||
255 | def search(self, query): |
|
|||
256 |
|
||||
257 | def changelist(**map): |
|
|||
258 | cl = self.repo.changelog |
|
|||
259 | count = 0 |
|
|||
260 | qw = query.lower().split() |
|
|||
261 |
|
||||
262 | def revgen(): |
|
|||
263 | for i in range(cl.count() - 1, 0, -100): |
|
|||
264 | l = [] |
|
|||
265 | for j in range(max(0, i - 100), i): |
|
|||
266 | n = cl.node(j) |
|
|||
267 | changes = cl.read(n) |
|
|||
268 | l.append((n, j, changes)) |
|
|||
269 | l.reverse() |
|
|||
270 | for e in l: |
|
|||
271 | yield e |
|
|||
272 |
|
||||
273 | for n, i, changes in revgen(): |
|
|||
274 | miss = 0 |
|
|||
275 | for q in qw: |
|
|||
276 | if not (q in changes[1].lower() or |
|
|||
277 | q in changes[4].lower() or |
|
|||
278 | q in " ".join(changes[3][:20]).lower()): |
|
|||
279 | miss = 1 |
|
|||
280 | break |
|
|||
281 | if miss: |
|
|||
282 | continue |
|
|||
283 |
|
||||
284 | count += 1 |
|
|||
285 | hn = hex(n) |
|
|||
286 |
|
||||
287 | yield self.t('searchentry', |
|
|||
288 | parity=count & 1, |
|
|||
289 | author=changes[1], |
|
|||
290 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
291 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
292 | changelogtag=self.showtag("changelogtag",n), |
|
|||
293 | manifest=hex(changes[0]), |
|
|||
294 | desc=changes[4], |
|
|||
295 | date=changes[2], |
|
|||
296 | files=self.listfilediffs(changes[3], n), |
|
|||
297 | rev=i, |
|
|||
298 | node=hn) |
|
|||
299 |
|
||||
300 | if count >= self.maxchanges: |
|
|||
301 | break |
|
|||
302 |
|
||||
303 | cl = self.repo.changelog |
|
|||
304 | mf = cl.read(cl.tip())[0] |
|
|||
305 |
|
||||
306 | yield self.t('search', |
|
|||
307 | query=query, |
|
|||
308 | manifest=hex(mf), |
|
|||
309 | entries=changelist) |
|
|||
310 |
|
||||
311 | def changeset(self, nodeid): |
|
|||
312 | cl = self.repo.changelog |
|
|||
313 | n = self.repo.lookup(nodeid) |
|
|||
314 | nodeid = hex(n) |
|
|||
315 | changes = cl.read(n) |
|
|||
316 | p1 = cl.parents(n)[0] |
|
|||
317 |
|
||||
318 | files = [] |
|
|||
319 | mf = self.repo.manifest.read(changes[0]) |
|
|||
320 | for f in changes[3]: |
|
|||
321 | files.append(self.t("filenodelink", |
|
|||
322 | filenode=hex(mf.get(f, nullid)), file=f)) |
|
|||
323 |
|
||||
324 | def diff(**map): |
|
|||
325 | yield self.diff(p1, n, None) |
|
|||
326 |
|
||||
327 | yield self.t('changeset', |
|
|||
328 | diff=diff, |
|
|||
329 | rev=cl.rev(n), |
|
|||
330 | node=nodeid, |
|
|||
331 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
332 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
333 | changesettag=self.showtag("changesettag",n), |
|
|||
334 | manifest=hex(changes[0]), |
|
|||
335 | author=changes[1], |
|
|||
336 | desc=changes[4], |
|
|||
337 | date=changes[2], |
|
|||
338 | files=files, |
|
|||
339 | archives=self.archivelist(nodeid)) |
|
|||
340 |
|
||||
341 | def filelog(self, f, filenode): |
|
|||
342 | cl = self.repo.changelog |
|
|||
343 | fl = self.repo.file(f) |
|
|||
344 | filenode = hex(fl.lookup(filenode)) |
|
|||
345 | count = fl.count() |
|
|||
346 |
|
||||
347 | def entries(**map): |
|
|||
348 | l = [] |
|
|||
349 | parity = (count - 1) & 1 |
|
|||
350 |
|
||||
351 | for i in range(count): |
|
|||
352 | n = fl.node(i) |
|
|||
353 | lr = fl.linkrev(n) |
|
|||
354 | cn = cl.node(lr) |
|
|||
355 | cs = cl.read(cl.node(lr)) |
|
|||
356 |
|
||||
357 | l.insert(0, {"parity": parity, |
|
|||
358 | "filenode": hex(n), |
|
|||
359 | "filerev": i, |
|
|||
360 | "file": f, |
|
|||
361 | "node": hex(cn), |
|
|||
362 | "author": cs[1], |
|
|||
363 | "date": cs[2], |
|
|||
364 | "rename": self.renamelink(fl, n), |
|
|||
365 | "parent": self.siblings(fl.parents(n), |
|
|||
366 | fl.rev, file=f), |
|
|||
367 | "child": self.siblings(fl.children(n), |
|
|||
368 | fl.rev, file=f), |
|
|||
369 | "desc": cs[4]}) |
|
|||
370 | parity = 1 - parity |
|
|||
371 |
|
||||
372 | for e in l: |
|
|||
373 | yield e |
|
|||
374 |
|
||||
375 | yield self.t("filelog", file=f, filenode=filenode, entries=entries) |
|
|||
376 |
|
||||
377 | def filerevision(self, f, node): |
|
|||
378 | fl = self.repo.file(f) |
|
|||
379 | n = fl.lookup(node) |
|
|||
380 | node = hex(n) |
|
|||
381 | text = fl.read(n) |
|
|||
382 | changerev = fl.linkrev(n) |
|
|||
383 | cl = self.repo.changelog |
|
|||
384 | cn = cl.node(changerev) |
|
|||
385 | cs = cl.read(cn) |
|
|||
386 | mfn = cs[0] |
|
|||
387 |
|
||||
388 | mt = mimetypes.guess_type(f)[0] |
|
|||
389 | rawtext = text |
|
|||
390 | if util.binary(text): |
|
|||
391 | mt = mt or 'application/octet-stream' |
|
|||
392 | text = "(binary:%s)" % mt |
|
|||
393 | mt = mt or 'text/plain' |
|
|||
394 |
|
||||
395 | def lines(): |
|
|||
396 | for l, t in enumerate(text.splitlines(1)): |
|
|||
397 | yield {"line": t, |
|
|||
398 | "linenumber": "% 6d" % (l + 1), |
|
|||
399 | "parity": l & 1} |
|
|||
400 |
|
||||
401 | yield self.t("filerevision", |
|
|||
402 | file=f, |
|
|||
403 | filenode=node, |
|
|||
404 | path=up(f), |
|
|||
405 | text=lines(), |
|
|||
406 | raw=rawtext, |
|
|||
407 | mimetype=mt, |
|
|||
408 | rev=changerev, |
|
|||
409 | node=hex(cn), |
|
|||
410 | manifest=hex(mfn), |
|
|||
411 | author=cs[1], |
|
|||
412 | date=cs[2], |
|
|||
413 | parent=self.siblings(fl.parents(n), fl.rev, file=f), |
|
|||
414 | child=self.siblings(fl.children(n), fl.rev, file=f), |
|
|||
415 | rename=self.renamelink(fl, n), |
|
|||
416 | permissions=self.repo.manifest.readflags(mfn)[f]) |
|
|||
417 |
|
||||
418 | def fileannotate(self, f, node): |
|
|||
419 | bcache = {} |
|
|||
420 | ncache = {} |
|
|||
421 | fl = self.repo.file(f) |
|
|||
422 | n = fl.lookup(node) |
|
|||
423 | node = hex(n) |
|
|||
424 | changerev = fl.linkrev(n) |
|
|||
425 |
|
||||
426 | cl = self.repo.changelog |
|
|||
427 | cn = cl.node(changerev) |
|
|||
428 | cs = cl.read(cn) |
|
|||
429 | mfn = cs[0] |
|
|||
430 |
|
||||
431 | def annotate(**map): |
|
|||
432 | parity = 1 |
|
|||
433 | last = None |
|
|||
434 | for r, l in fl.annotate(n): |
|
|||
435 | try: |
|
|||
436 | cnode = ncache[r] |
|
|||
437 | except KeyError: |
|
|||
438 | cnode = ncache[r] = self.repo.changelog.node(r) |
|
|||
439 |
|
||||
440 | try: |
|
|||
441 | name = bcache[r] |
|
|||
442 | except KeyError: |
|
|||
443 | cl = self.repo.changelog.read(cnode) |
|
|||
444 | bcache[r] = name = self.repo.ui.shortuser(cl[1]) |
|
|||
445 |
|
||||
446 | if last != cnode: |
|
|||
447 | parity = 1 - parity |
|
|||
448 | last = cnode |
|
|||
449 |
|
||||
450 | yield {"parity": parity, |
|
|||
451 | "node": hex(cnode), |
|
|||
452 | "rev": r, |
|
|||
453 | "author": name, |
|
|||
454 | "file": f, |
|
|||
455 | "line": l} |
|
|||
456 |
|
||||
457 | yield self.t("fileannotate", |
|
|||
458 | file=f, |
|
|||
459 | filenode=node, |
|
|||
460 | annotate=annotate, |
|
|||
461 | path=up(f), |
|
|||
462 | rev=changerev, |
|
|||
463 | node=hex(cn), |
|
|||
464 | manifest=hex(mfn), |
|
|||
465 | author=cs[1], |
|
|||
466 | date=cs[2], |
|
|||
467 | rename=self.renamelink(fl, n), |
|
|||
468 | parent=self.siblings(fl.parents(n), fl.rev, file=f), |
|
|||
469 | child=self.siblings(fl.children(n), fl.rev, file=f), |
|
|||
470 | permissions=self.repo.manifest.readflags(mfn)[f]) |
|
|||
471 |
|
||||
472 | def manifest(self, mnode, path): |
|
|||
473 | man = self.repo.manifest |
|
|||
474 | mn = man.lookup(mnode) |
|
|||
475 | mnode = hex(mn) |
|
|||
476 | mf = man.read(mn) |
|
|||
477 | rev = man.rev(mn) |
|
|||
478 | changerev = man.linkrev(mn) |
|
|||
479 | node = self.repo.changelog.node(changerev) |
|
|||
480 | mff = man.readflags(mn) |
|
|||
481 |
|
||||
482 | files = {} |
|
|||
483 |
|
||||
484 | p = path[1:] |
|
|||
485 | if p and p[-1] != "/": |
|
|||
486 | p += "/" |
|
|||
487 | l = len(p) |
|
|||
488 |
|
||||
489 | for f,n in mf.items(): |
|
|||
490 | if f[:l] != p: |
|
|||
491 | continue |
|
|||
492 | remain = f[l:] |
|
|||
493 | if "/" in remain: |
|
|||
494 | short = remain[:remain.find("/") + 1] # bleah |
|
|||
495 | files[short] = (f, None) |
|
|||
496 | else: |
|
|||
497 | short = os.path.basename(remain) |
|
|||
498 | files[short] = (f, n) |
|
|||
499 |
|
||||
500 | def filelist(**map): |
|
|||
501 | parity = 0 |
|
|||
502 | fl = files.keys() |
|
|||
503 | fl.sort() |
|
|||
504 | for f in fl: |
|
|||
505 | full, fnode = files[f] |
|
|||
506 | if not fnode: |
|
|||
507 | continue |
|
|||
508 |
|
||||
509 | yield {"file": full, |
|
|||
510 | "manifest": mnode, |
|
|||
511 | "filenode": hex(fnode), |
|
|||
512 | "parity": parity, |
|
|||
513 | "basename": f, |
|
|||
514 | "permissions": mff[full]} |
|
|||
515 | parity = 1 - parity |
|
|||
516 |
|
||||
517 | def dirlist(**map): |
|
|||
518 | parity = 0 |
|
|||
519 | fl = files.keys() |
|
|||
520 | fl.sort() |
|
|||
521 | for f in fl: |
|
|||
522 | full, fnode = files[f] |
|
|||
523 | if fnode: |
|
|||
524 | continue |
|
|||
525 |
|
||||
526 | yield {"parity": parity, |
|
|||
527 | "path": os.path.join(path, f), |
|
|||
528 | "manifest": mnode, |
|
|||
529 | "basename": f[:-1]} |
|
|||
530 | parity = 1 - parity |
|
|||
531 |
|
||||
532 | yield self.t("manifest", |
|
|||
533 | manifest=mnode, |
|
|||
534 | rev=rev, |
|
|||
535 | node=hex(node), |
|
|||
536 | path=path, |
|
|||
537 | up=up(path), |
|
|||
538 | fentries=filelist, |
|
|||
539 | dentries=dirlist, |
|
|||
540 | archives=self.archivelist(hex(node))) |
|
|||
541 |
|
||||
542 | def tags(self): |
|
|||
543 | cl = self.repo.changelog |
|
|||
544 | mf = cl.read(cl.tip())[0] |
|
|||
545 |
|
||||
546 | i = self.repo.tagslist() |
|
|||
547 | i.reverse() |
|
|||
548 |
|
||||
549 | def entries(notip=False, **map): |
|
|||
550 | parity = 0 |
|
|||
551 | for k,n in i: |
|
|||
552 | if notip and k == "tip": continue |
|
|||
553 | yield {"parity": parity, |
|
|||
554 | "tag": k, |
|
|||
555 | "tagmanifest": hex(cl.read(n)[0]), |
|
|||
556 | "date": cl.read(n)[2], |
|
|||
557 | "node": hex(n)} |
|
|||
558 | parity = 1 - parity |
|
|||
559 |
|
||||
560 | yield self.t("tags", |
|
|||
561 | manifest=hex(mf), |
|
|||
562 | entries=lambda **x: entries(False, **x), |
|
|||
563 | entriesnotip=lambda **x: entries(True, **x)) |
|
|||
564 |
|
||||
565 | def summary(self): |
|
|||
566 | cl = self.repo.changelog |
|
|||
567 | mf = cl.read(cl.tip())[0] |
|
|||
568 |
|
||||
569 | i = self.repo.tagslist() |
|
|||
570 | i.reverse() |
|
|||
571 |
|
||||
572 | def tagentries(**map): |
|
|||
573 | parity = 0 |
|
|||
574 | count = 0 |
|
|||
575 | for k,n in i: |
|
|||
576 | if k == "tip": # skip tip |
|
|||
577 | continue; |
|
|||
578 |
|
||||
579 | count += 1 |
|
|||
580 | if count > 10: # limit to 10 tags |
|
|||
581 | break; |
|
|||
582 |
|
||||
583 | c = cl.read(n) |
|
|||
584 | m = c[0] |
|
|||
585 | t = c[2] |
|
|||
586 |
|
||||
587 | yield self.t("tagentry", |
|
|||
588 | parity = parity, |
|
|||
589 | tag = k, |
|
|||
590 | node = hex(n), |
|
|||
591 | date = t, |
|
|||
592 | tagmanifest = hex(m)) |
|
|||
593 | parity = 1 - parity |
|
|||
594 |
|
||||
595 | def changelist(**map): |
|
|||
596 | parity = 0 |
|
|||
597 | cl = self.repo.changelog |
|
|||
598 | l = [] # build a list in forward order for efficiency |
|
|||
599 | for i in range(start, end): |
|
|||
600 | n = cl.node(i) |
|
|||
601 | changes = cl.read(n) |
|
|||
602 | hn = hex(n) |
|
|||
603 | t = changes[2] |
|
|||
604 |
|
||||
605 | l.insert(0, self.t( |
|
|||
606 | 'shortlogentry', |
|
|||
607 | parity = parity, |
|
|||
608 | author = changes[1], |
|
|||
609 | manifest = hex(changes[0]), |
|
|||
610 | desc = changes[4], |
|
|||
611 | date = t, |
|
|||
612 | rev = i, |
|
|||
613 | node = hn)) |
|
|||
614 | parity = 1 - parity |
|
|||
615 |
|
||||
616 | yield l |
|
|||
617 |
|
||||
618 | cl = self.repo.changelog |
|
|||
619 | mf = cl.read(cl.tip())[0] |
|
|||
620 | count = cl.count() |
|
|||
621 | start = max(0, count - self.maxchanges) |
|
|||
622 | end = min(count, start + self.maxchanges) |
|
|||
623 | pos = end - 1 |
|
|||
624 |
|
||||
625 | yield self.t("summary", |
|
|||
626 | desc = self.repo.ui.config("web", "description", "unknown"), |
|
|||
627 | owner = (self.repo.ui.config("ui", "username") or # preferred |
|
|||
628 | self.repo.ui.config("web", "contact") or # deprecated |
|
|||
629 | self.repo.ui.config("web", "author", "unknown")), # also |
|
|||
630 | lastchange = (0, 0), # FIXME |
|
|||
631 | manifest = hex(mf), |
|
|||
632 | tags = tagentries, |
|
|||
633 | shortlog = changelist) |
|
|||
634 |
|
||||
635 | def filediff(self, file, changeset): |
|
|||
636 | cl = self.repo.changelog |
|
|||
637 | n = self.repo.lookup(changeset) |
|
|||
638 | changeset = hex(n) |
|
|||
639 | p1 = cl.parents(n)[0] |
|
|||
640 | cs = cl.read(n) |
|
|||
641 | mf = self.repo.manifest.read(cs[0]) |
|
|||
642 |
|
||||
643 | def diff(**map): |
|
|||
644 | yield self.diff(p1, n, [file]) |
|
|||
645 |
|
||||
646 | yield self.t("filediff", |
|
|||
647 | file=file, |
|
|||
648 | filenode=hex(mf.get(file, nullid)), |
|
|||
649 | node=changeset, |
|
|||
650 | rev=self.repo.changelog.rev(n), |
|
|||
651 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
652 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
653 | diff=diff) |
|
|||
654 |
|
||||
655 | archive_specs = { |
|
|||
656 | 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', 'x-bzip2'), |
|
|||
657 | 'gz': ('application/x-tar', 'tgz', '.tar.gz', 'x-gzip'), |
|
|||
658 | 'zip': ('application/zip', 'zip', '.zip', None), |
|
|||
659 | } |
|
|||
660 |
|
||||
661 | def archive(self, req, cnode, type): |
|
|||
662 | reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame)) |
|
|||
663 | name = "%s-%s" % (reponame, short(cnode)) |
|
|||
664 | mimetype, artype, extension, encoding = self.archive_specs[type] |
|
|||
665 | headers = [('Content-type', mimetype), |
|
|||
666 | ('Content-disposition', 'attachment; filename=%s%s' % |
|
|||
667 | (name, extension))] |
|
|||
668 | if encoding: |
|
|||
669 | headers.append(('Content-encoding', encoding)) |
|
|||
670 | req.header(headers) |
|
|||
671 | archival.archive(self.repo, req.out, cnode, artype, prefix=name) |
|
|||
672 |
|
||||
673 | # add tags to things |
|
|||
674 | # tags -> list of changesets corresponding to tags |
|
|||
675 | # find tag, changeset, file |
|
|||
676 |
|
||||
677 | def run(self, req=hgrequest()): |
|
|||
678 | def clean(path): |
|
|||
679 | p = util.normpath(path) |
|
|||
680 | if p[:2] == "..": |
|
|||
681 | raise "suspicious path" |
|
|||
682 | return p |
|
|||
683 |
|
||||
684 | def header(**map): |
|
|||
685 | yield self.t("header", **map) |
|
|||
686 |
|
||||
687 | def footer(**map): |
|
|||
688 | yield self.t("footer", |
|
|||
689 | motd=self.repo.ui.config("web", "motd", ""), |
|
|||
690 | **map) |
|
|||
691 |
|
||||
692 | def expand_form(form): |
|
|||
693 | shortcuts = { |
|
|||
694 | 'cl': [('cmd', ['changelog']), ('rev', None)], |
|
|||
695 | 'cs': [('cmd', ['changeset']), ('node', None)], |
|
|||
696 | 'f': [('cmd', ['file']), ('filenode', None)], |
|
|||
697 | 'fl': [('cmd', ['filelog']), ('filenode', None)], |
|
|||
698 | 'fd': [('cmd', ['filediff']), ('node', None)], |
|
|||
699 | 'fa': [('cmd', ['annotate']), ('filenode', None)], |
|
|||
700 | 'mf': [('cmd', ['manifest']), ('manifest', None)], |
|
|||
701 | 'ca': [('cmd', ['archive']), ('node', None)], |
|
|||
702 | 'tags': [('cmd', ['tags'])], |
|
|||
703 | 'tip': [('cmd', ['changeset']), ('node', ['tip'])], |
|
|||
704 | 'static': [('cmd', ['static']), ('file', None)] |
|
|||
705 | } |
|
|||
706 |
|
||||
707 | for k in shortcuts.iterkeys(): |
|
|||
708 | if form.has_key(k): |
|
|||
709 | for name, value in shortcuts[k]: |
|
|||
710 | if value is None: |
|
|||
711 | value = form[k] |
|
|||
712 | form[name] = value |
|
|||
713 | del form[k] |
|
|||
714 |
|
||||
715 | self.refresh() |
|
|||
716 |
|
||||
717 | expand_form(req.form) |
|
|||
718 |
|
||||
719 | t = self.repo.ui.config("web", "templates", templater.templatepath()) |
|
|||
720 | static = self.repo.ui.config("web", "static", os.path.join(t,"static")) |
|
|||
721 | m = os.path.join(t, "map") |
|
|||
722 | style = self.repo.ui.config("web", "style", "") |
|
|||
723 | if req.form.has_key('style'): |
|
|||
724 | style = req.form['style'][0] |
|
|||
725 | if style: |
|
|||
726 | b = os.path.basename("map-" + style) |
|
|||
727 | p = os.path.join(t, b) |
|
|||
728 | if os.path.isfile(p): |
|
|||
729 | m = p |
|
|||
730 |
|
||||
731 | port = req.env["SERVER_PORT"] |
|
|||
732 | port = port != "80" and (":" + port) or "" |
|
|||
733 | uri = req.env["REQUEST_URI"] |
|
|||
734 | if "?" in uri: |
|
|||
735 | uri = uri.split("?")[0] |
|
|||
736 | url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri) |
|
|||
737 | if not self.reponame: |
|
|||
738 | self.reponame = (self.repo.ui.config("web", "name") |
|
|||
739 | or uri.strip('/') or self.repo.root) |
|
|||
740 |
|
||||
741 | self.t = templater.templater(m, templater.common_filters, |
|
|||
742 | defaults={"url": url, |
|
|||
743 | "repo": self.reponame, |
|
|||
744 | "header": header, |
|
|||
745 | "footer": footer, |
|
|||
746 | }) |
|
|||
747 |
|
||||
748 | if not req.form.has_key('cmd'): |
|
|||
749 | req.form['cmd'] = [self.t.cache['default'],] |
|
|||
750 |
|
||||
751 | cmd = req.form['cmd'][0] |
|
|||
752 | if cmd == 'changelog': |
|
|||
753 | hi = self.repo.changelog.count() - 1 |
|
|||
754 | if req.form.has_key('rev'): |
|
|||
755 | hi = req.form['rev'][0] |
|
|||
756 | try: |
|
|||
757 | hi = self.repo.changelog.rev(self.repo.lookup(hi)) |
|
|||
758 | except hg.RepoError: |
|
|||
759 | req.write(self.search(hi)) # XXX redirect to 404 page? |
|
|||
760 | return |
|
|||
761 |
|
||||
762 | req.write(self.changelog(hi)) |
|
|||
763 |
|
||||
764 | elif cmd == 'changeset': |
|
|||
765 | req.write(self.changeset(req.form['node'][0])) |
|
|||
766 |
|
||||
767 | elif cmd == 'manifest': |
|
|||
768 | req.write(self.manifest(req.form['manifest'][0], |
|
|||
769 | clean(req.form['path'][0]))) |
|
|||
770 |
|
||||
771 | elif cmd == 'tags': |
|
|||
772 | req.write(self.tags()) |
|
|||
773 |
|
||||
774 | elif cmd == 'summary': |
|
|||
775 | req.write(self.summary()) |
|
|||
776 |
|
||||
777 | elif cmd == 'filediff': |
|
|||
778 | req.write(self.filediff(clean(req.form['file'][0]), |
|
|||
779 | req.form['node'][0])) |
|
|||
780 |
|
||||
781 | elif cmd == 'file': |
|
|||
782 | req.write(self.filerevision(clean(req.form['file'][0]), |
|
|||
783 | req.form['filenode'][0])) |
|
|||
784 |
|
||||
785 | elif cmd == 'annotate': |
|
|||
786 | req.write(self.fileannotate(clean(req.form['file'][0]), |
|
|||
787 | req.form['filenode'][0])) |
|
|||
788 |
|
||||
789 | elif cmd == 'filelog': |
|
|||
790 | req.write(self.filelog(clean(req.form['file'][0]), |
|
|||
791 | req.form['filenode'][0])) |
|
|||
792 |
|
||||
793 | elif cmd == 'heads': |
|
|||
794 | req.httphdr("application/mercurial-0.1") |
|
|||
795 | h = self.repo.heads() |
|
|||
796 | req.write(" ".join(map(hex, h)) + "\n") |
|
|||
797 |
|
||||
798 | elif cmd == 'branches': |
|
|||
799 | req.httphdr("application/mercurial-0.1") |
|
|||
800 | nodes = [] |
|
|||
801 | if req.form.has_key('nodes'): |
|
|||
802 | nodes = map(bin, req.form['nodes'][0].split(" ")) |
|
|||
803 | for b in self.repo.branches(nodes): |
|
|||
804 | req.write(" ".join(map(hex, b)) + "\n") |
|
|||
805 |
|
||||
806 | elif cmd == 'between': |
|
|||
807 | req.httphdr("application/mercurial-0.1") |
|
|||
808 | nodes = [] |
|
|||
809 | if req.form.has_key('pairs'): |
|
|||
810 | pairs = [map(bin, p.split("-")) |
|
|||
811 | for p in req.form['pairs'][0].split(" ")] |
|
|||
812 | for b in self.repo.between(pairs): |
|
|||
813 | req.write(" ".join(map(hex, b)) + "\n") |
|
|||
814 |
|
||||
815 | elif cmd == 'changegroup': |
|
|||
816 | req.httphdr("application/mercurial-0.1") |
|
|||
817 | nodes = [] |
|
|||
818 | if not self.allowpull: |
|
|||
819 | return |
|
|||
820 |
|
||||
821 | if req.form.has_key('roots'): |
|
|||
822 | nodes = map(bin, req.form['roots'][0].split(" ")) |
|
|||
823 |
|
||||
824 | z = zlib.compressobj() |
|
|||
825 | f = self.repo.changegroup(nodes, 'serve') |
|
|||
826 | while 1: |
|
|||
827 | chunk = f.read(4096) |
|
|||
828 | if not chunk: |
|
|||
829 | break |
|
|||
830 | req.write(z.compress(chunk)) |
|
|||
831 |
|
||||
832 | req.write(z.flush()) |
|
|||
833 |
|
||||
834 | elif cmd == 'archive': |
|
|||
835 | changeset = self.repo.lookup(req.form['node'][0]) |
|
|||
836 | type = req.form['type'][0] |
|
|||
837 | if (type in self.archives and |
|
|||
838 | self.repo.ui.configbool("web", "allow" + type, False)): |
|
|||
839 | self.archive(req, changeset, type) |
|
|||
840 | return |
|
|||
841 |
|
||||
842 | req.write(self.t("error")) |
|
|||
843 |
|
||||
844 | elif cmd == 'static': |
|
|||
845 | fname = req.form['file'][0] |
|
|||
846 | req.write(staticfile(static, fname) |
|
|||
847 | or self.t("error", error="%r not found" % fname)) |
|
|||
848 |
|
||||
849 | else: |
|
|||
850 | req.write(self.t("error")) |
|
|||
851 |
|
||||
852 | # This is a stopgap |
|
|||
853 | class hgwebdir(object): |
|
|||
854 | def __init__(self, config): |
|
|||
855 | def cleannames(items): |
|
|||
856 | return [(name.strip(os.sep), path) for name, path in items] |
|
|||
857 |
|
||||
858 | self.motd = "" |
|
|||
859 | self.repos_sorted = ('name', False) |
|
|||
860 | if isinstance(config, (list, tuple)): |
|
|||
861 | self.repos = cleannames(config) |
|
|||
862 | self.repos_sorted = ('', False) |
|
|||
863 | elif isinstance(config, dict): |
|
|||
864 | self.repos = cleannames(config.items()) |
|
|||
865 | self.repos.sort() |
|
|||
866 | else: |
|
|||
867 | cp = ConfigParser.SafeConfigParser() |
|
|||
868 | cp.read(config) |
|
|||
869 | self.repos = [] |
|
|||
870 | if cp.has_section('web') and cp.has_option('web', 'motd'): |
|
|||
871 | self.motd = cp.get('web', 'motd') |
|
|||
872 | if cp.has_section('paths'): |
|
|||
873 | self.repos.extend(cleannames(cp.items('paths'))) |
|
|||
874 | if cp.has_section('collections'): |
|
|||
875 | for prefix, root in cp.items('collections'): |
|
|||
876 | for path in util.walkrepos(root): |
|
|||
877 | repo = os.path.normpath(path) |
|
|||
878 | name = repo |
|
|||
879 | if name.startswith(prefix): |
|
|||
880 | name = name[len(prefix):] |
|
|||
881 | self.repos.append((name.lstrip(os.sep), repo)) |
|
|||
882 | self.repos.sort() |
|
|||
883 |
|
||||
884 | def run(self, req=hgrequest()): |
|
|||
885 | def header(**map): |
|
|||
886 | yield tmpl("header", **map) |
|
|||
887 |
|
||||
888 | def footer(**map): |
|
|||
889 | yield tmpl("footer", motd=self.motd, **map) |
|
|||
890 |
|
||||
891 | m = os.path.join(templater.templatepath(), "map") |
|
|||
892 | tmpl = templater.templater(m, templater.common_filters, |
|
|||
893 | defaults={"header": header, |
|
|||
894 | "footer": footer}) |
|
|||
895 |
|
||||
896 | def archivelist(ui, nodeid, url): |
|
|||
897 | for i in ['zip', 'gz', 'bz2']: |
|
|||
898 | if ui.configbool("web", "allow" + i, False): |
|
|||
899 | yield {"type" : i, "node": nodeid, "url": url} |
|
|||
900 |
|
||||
901 | def entries(sortcolumn="", descending=False, **map): |
|
|||
902 | rows = [] |
|
|||
903 | parity = 0 |
|
|||
904 | for name, path in self.repos: |
|
|||
905 | u = ui.ui() |
|
|||
906 | try: |
|
|||
907 | u.readconfig(os.path.join(path, '.hg', 'hgrc')) |
|
|||
908 | except IOError: |
|
|||
909 | pass |
|
|||
910 | get = u.config |
|
|||
911 |
|
||||
912 | url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name]) |
|
|||
913 | .replace("//", "/")) |
|
|||
914 |
|
||||
915 | # update time with local timezone |
|
|||
916 | try: |
|
|||
917 | d = (get_mtime(path), util.makedate()[1]) |
|
|||
918 | except OSError: |
|
|||
919 | continue |
|
|||
920 |
|
||||
921 | contact = (get("ui", "username") or # preferred |
|
|||
922 | get("web", "contact") or # deprecated |
|
|||
923 | get("web", "author", "")) # also |
|
|||
924 | description = get("web", "description", "") |
|
|||
925 | name = get("web", "name", name) |
|
|||
926 | row = dict(contact=contact or "unknown", |
|
|||
927 | contact_sort=contact.upper() or "unknown", |
|
|||
928 | name=name, |
|
|||
929 | name_sort=name, |
|
|||
930 | url=url, |
|
|||
931 | description=description or "unknown", |
|
|||
932 | description_sort=description.upper() or "unknown", |
|
|||
933 | lastchange=d, |
|
|||
934 | lastchange_sort=d[1]-d[0], |
|
|||
935 | archives=archivelist(u, "tip", url)) |
|
|||
936 | if (not sortcolumn |
|
|||
937 | or (sortcolumn, descending) == self.repos_sorted): |
|
|||
938 | # fast path for unsorted output |
|
|||
939 | row['parity'] = parity |
|
|||
940 | parity = 1 - parity |
|
|||
941 | yield row |
|
|||
942 | else: |
|
|||
943 | rows.append((row["%s_sort" % sortcolumn], row)) |
|
|||
944 | if rows: |
|
|||
945 | rows.sort() |
|
|||
946 | if descending: |
|
|||
947 | rows.reverse() |
|
|||
948 | for key, row in rows: |
|
|||
949 | row['parity'] = parity |
|
|||
950 | parity = 1 - parity |
|
|||
951 | yield row |
|
|||
952 |
|
||||
953 | virtual = req.env.get("PATH_INFO", "").strip('/') |
|
|||
954 | if virtual: |
|
|||
955 | real = dict(self.repos).get(virtual) |
|
|||
956 | if real: |
|
|||
957 | try: |
|
|||
958 | hgweb(real).run(req) |
|
|||
959 | except IOError, inst: |
|
|||
960 | req.write(tmpl("error", error=inst.strerror)) |
|
|||
961 | except hg.RepoError, inst: |
|
|||
962 | req.write(tmpl("error", error=str(inst))) |
|
|||
963 | else: |
|
|||
964 | req.write(tmpl("notfound", repo=virtual)) |
|
|||
965 | else: |
|
|||
966 | if req.form.has_key('static'): |
|
|||
967 | static = os.path.join(templater.templatepath(), "static") |
|
|||
968 | fname = req.form['static'][0] |
|
|||
969 | req.write(staticfile(static, fname) |
|
|||
970 | or tmpl("error", error="%r not found" % fname)) |
|
|||
971 | else: |
|
|||
972 | sortable = ["name", "description", "contact", "lastchange"] |
|
|||
973 | sortcolumn, descending = self.repos_sorted |
|
|||
974 | if req.form.has_key('sort'): |
|
|||
975 | sortcolumn = req.form['sort'][0] |
|
|||
976 | descending = sortcolumn.startswith('-') |
|
|||
977 | if descending: |
|
|||
978 | sortcolumn = sortcolumn[1:] |
|
|||
979 | if sortcolumn not in sortable: |
|
|||
980 | sortcolumn = "" |
|
|||
981 |
|
||||
982 | sort = [("sort_%s" % column, |
|
|||
983 | "%s%s" % ((not descending and column == sortcolumn) |
|
|||
984 | and "-" or "", column)) |
|
|||
985 | for column in sortable] |
|
|||
986 | req.write(tmpl("index", entries=entries, |
|
|||
987 | sortcolumn=sortcolumn, descending=descending, |
|
|||
988 | **dict(sort))) |
|
@@ -6,17 +6,18 b'' | |||||
6 | # This software may be used and distributed according to the terms |
|
6 | # This software may be used and distributed according to the terms | |
7 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | # of the GNU General Public License, incorporated herein by reference. | |
8 |
|
8 | |||
9 |
import os |
|
9 | import os | |
|
10 | import os.path | |||
10 | import mimetypes |
|
11 | import mimetypes | |
11 | from mercurial.demandload import demandload |
|
12 | from mercurial.demandload import demandload | |
12 |
demandload(globals(), " |
|
13 | demandload(globals(), "re zlib ConfigParser") | |
13 | demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater") |
|
14 | demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater") | |
14 | demandload(globals(), "mercurial.hgweb.request:hgrequest") |
|
15 | demandload(globals(), "mercurial.hgweb.request:hgrequest") | |
15 |
demandload(globals(), "mercurial.hgweb. |
|
16 | demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile") | |
16 | from mercurial.node import * |
|
17 | from mercurial.node import * | |
17 | from mercurial.i18n import gettext as _ |
|
18 | from mercurial.i18n import gettext as _ | |
18 |
|
19 | |||
19 | def up(p): |
|
20 | def _up(p): | |
20 | if p[0] != "/": |
|
21 | if p[0] != "/": | |
21 | p = "/" + p |
|
22 | p = "/" + p | |
22 | if p[-1] == "/": |
|
23 | if p[-1] == "/": | |
@@ -26,38 +27,6 b' def up(p):' | |||||
26 | return "/" |
|
27 | return "/" | |
27 | return up + "/" |
|
28 | return up + "/" | |
28 |
|
29 | |||
29 | def get_mtime(repo_path): |
|
|||
30 | hg_path = os.path.join(repo_path, ".hg") |
|
|||
31 | cl_path = os.path.join(hg_path, "00changelog.i") |
|
|||
32 | if os.path.exists(os.path.join(cl_path)): |
|
|||
33 | return os.stat(cl_path).st_mtime |
|
|||
34 | else: |
|
|||
35 | return os.stat(hg_path).st_mtime |
|
|||
36 |
|
||||
37 | def staticfile(directory, fname): |
|
|||
38 | """return a file inside directory with guessed content-type header |
|
|||
39 |
|
||||
40 | fname always uses '/' as directory separator and isn't allowed to |
|
|||
41 | contain unusual path components. |
|
|||
42 | Content-type is guessed using the mimetypes module. |
|
|||
43 | Return an empty string if fname is illegal or file not found. |
|
|||
44 |
|
||||
45 | """ |
|
|||
46 | parts = fname.split('/') |
|
|||
47 | path = directory |
|
|||
48 | for part in parts: |
|
|||
49 | if (part in ('', os.curdir, os.pardir) or |
|
|||
50 | os.sep in part or os.altsep is not None and os.altsep in part): |
|
|||
51 | return "" |
|
|||
52 | path = os.path.join(path, part) |
|
|||
53 | try: |
|
|||
54 | os.stat(path) |
|
|||
55 | ct = mimetypes.guess_type(path)[0] or "text/plain" |
|
|||
56 | return "Content-type: %s\n\n%s" % (ct, file(path).read()) |
|
|||
57 | except (TypeError, OSError): |
|
|||
58 | # illegal fname or unreadable file |
|
|||
59 | return "" |
|
|||
60 |
|
||||
61 | class hgweb(object): |
|
30 | class hgweb(object): | |
62 | def __init__(self, repo, name=None): |
|
31 | def __init__(self, repo, name=None): | |
63 | if type(repo) == type(""): |
|
32 | if type(repo) == type(""): | |
@@ -401,7 +370,7 b' class hgweb(object):' | |||||
401 | yield self.t("filerevision", |
|
370 | yield self.t("filerevision", | |
402 | file=f, |
|
371 | file=f, | |
403 | filenode=node, |
|
372 | filenode=node, | |
404 | path=up(f), |
|
373 | path=_up(f), | |
405 | text=lines(), |
|
374 | text=lines(), | |
406 | raw=rawtext, |
|
375 | raw=rawtext, | |
407 | mimetype=mt, |
|
376 | mimetype=mt, | |
@@ -458,7 +427,7 b' class hgweb(object):' | |||||
458 | file=f, |
|
427 | file=f, | |
459 | filenode=node, |
|
428 | filenode=node, | |
460 | annotate=annotate, |
|
429 | annotate=annotate, | |
461 | path=up(f), |
|
430 | path=_up(f), | |
462 | rev=changerev, |
|
431 | rev=changerev, | |
463 | node=hex(cn), |
|
432 | node=hex(cn), | |
464 | manifest=hex(mfn), |
|
433 | manifest=hex(mfn), | |
@@ -534,7 +503,7 b' class hgweb(object):' | |||||
534 | rev=rev, |
|
503 | rev=rev, | |
535 | node=hex(node), |
|
504 | node=hex(node), | |
536 | path=path, |
|
505 | path=path, | |
537 | up=up(path), |
|
506 | up=_up(path), | |
538 | fentries=filelist, |
|
507 | fentries=filelist, | |
539 | dentries=dirlist, |
|
508 | dentries=dirlist, | |
540 | archives=self.archivelist(hex(node))) |
|
509 | archives=self.archivelist(hex(node))) | |
@@ -848,141 +817,3 b' class hgweb(object):' | |||||
848 |
|
817 | |||
849 | else: |
|
818 | else: | |
850 | req.write(self.t("error")) |
|
819 | req.write(self.t("error")) | |
851 |
|
||||
852 | # This is a stopgap |
|
|||
853 | class hgwebdir(object): |
|
|||
854 | def __init__(self, config): |
|
|||
855 | def cleannames(items): |
|
|||
856 | return [(name.strip(os.sep), path) for name, path in items] |
|
|||
857 |
|
||||
858 | self.motd = "" |
|
|||
859 | self.repos_sorted = ('name', False) |
|
|||
860 | if isinstance(config, (list, tuple)): |
|
|||
861 | self.repos = cleannames(config) |
|
|||
862 | self.repos_sorted = ('', False) |
|
|||
863 | elif isinstance(config, dict): |
|
|||
864 | self.repos = cleannames(config.items()) |
|
|||
865 | self.repos.sort() |
|
|||
866 | else: |
|
|||
867 | cp = ConfigParser.SafeConfigParser() |
|
|||
868 | cp.read(config) |
|
|||
869 | self.repos = [] |
|
|||
870 | if cp.has_section('web') and cp.has_option('web', 'motd'): |
|
|||
871 | self.motd = cp.get('web', 'motd') |
|
|||
872 | if cp.has_section('paths'): |
|
|||
873 | self.repos.extend(cleannames(cp.items('paths'))) |
|
|||
874 | if cp.has_section('collections'): |
|
|||
875 | for prefix, root in cp.items('collections'): |
|
|||
876 | for path in util.walkrepos(root): |
|
|||
877 | repo = os.path.normpath(path) |
|
|||
878 | name = repo |
|
|||
879 | if name.startswith(prefix): |
|
|||
880 | name = name[len(prefix):] |
|
|||
881 | self.repos.append((name.lstrip(os.sep), repo)) |
|
|||
882 | self.repos.sort() |
|
|||
883 |
|
||||
884 | def run(self, req=hgrequest()): |
|
|||
885 | def header(**map): |
|
|||
886 | yield tmpl("header", **map) |
|
|||
887 |
|
||||
888 | def footer(**map): |
|
|||
889 | yield tmpl("footer", motd=self.motd, **map) |
|
|||
890 |
|
||||
891 | m = os.path.join(templater.templatepath(), "map") |
|
|||
892 | tmpl = templater.templater(m, templater.common_filters, |
|
|||
893 | defaults={"header": header, |
|
|||
894 | "footer": footer}) |
|
|||
895 |
|
||||
896 | def archivelist(ui, nodeid, url): |
|
|||
897 | for i in ['zip', 'gz', 'bz2']: |
|
|||
898 | if ui.configbool("web", "allow" + i, False): |
|
|||
899 | yield {"type" : i, "node": nodeid, "url": url} |
|
|||
900 |
|
||||
901 | def entries(sortcolumn="", descending=False, **map): |
|
|||
902 | rows = [] |
|
|||
903 | parity = 0 |
|
|||
904 | for name, path in self.repos: |
|
|||
905 | u = ui.ui() |
|
|||
906 | try: |
|
|||
907 | u.readconfig(os.path.join(path, '.hg', 'hgrc')) |
|
|||
908 | except IOError: |
|
|||
909 | pass |
|
|||
910 | get = u.config |
|
|||
911 |
|
||||
912 | url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name]) |
|
|||
913 | .replace("//", "/")) |
|
|||
914 |
|
||||
915 | # update time with local timezone |
|
|||
916 | try: |
|
|||
917 | d = (get_mtime(path), util.makedate()[1]) |
|
|||
918 | except OSError: |
|
|||
919 | continue |
|
|||
920 |
|
||||
921 | contact = (get("ui", "username") or # preferred |
|
|||
922 | get("web", "contact") or # deprecated |
|
|||
923 | get("web", "author", "")) # also |
|
|||
924 | description = get("web", "description", "") |
|
|||
925 | name = get("web", "name", name) |
|
|||
926 | row = dict(contact=contact or "unknown", |
|
|||
927 | contact_sort=contact.upper() or "unknown", |
|
|||
928 | name=name, |
|
|||
929 | name_sort=name, |
|
|||
930 | url=url, |
|
|||
931 | description=description or "unknown", |
|
|||
932 | description_sort=description.upper() or "unknown", |
|
|||
933 | lastchange=d, |
|
|||
934 | lastchange_sort=d[1]-d[0], |
|
|||
935 | archives=archivelist(u, "tip", url)) |
|
|||
936 | if (not sortcolumn |
|
|||
937 | or (sortcolumn, descending) == self.repos_sorted): |
|
|||
938 | # fast path for unsorted output |
|
|||
939 | row['parity'] = parity |
|
|||
940 | parity = 1 - parity |
|
|||
941 | yield row |
|
|||
942 | else: |
|
|||
943 | rows.append((row["%s_sort" % sortcolumn], row)) |
|
|||
944 | if rows: |
|
|||
945 | rows.sort() |
|
|||
946 | if descending: |
|
|||
947 | rows.reverse() |
|
|||
948 | for key, row in rows: |
|
|||
949 | row['parity'] = parity |
|
|||
950 | parity = 1 - parity |
|
|||
951 | yield row |
|
|||
952 |
|
||||
953 | virtual = req.env.get("PATH_INFO", "").strip('/') |
|
|||
954 | if virtual: |
|
|||
955 | real = dict(self.repos).get(virtual) |
|
|||
956 | if real: |
|
|||
957 | try: |
|
|||
958 | hgweb(real).run(req) |
|
|||
959 | except IOError, inst: |
|
|||
960 | req.write(tmpl("error", error=inst.strerror)) |
|
|||
961 | except hg.RepoError, inst: |
|
|||
962 | req.write(tmpl("error", error=str(inst))) |
|
|||
963 | else: |
|
|||
964 | req.write(tmpl("notfound", repo=virtual)) |
|
|||
965 | else: |
|
|||
966 | if req.form.has_key('static'): |
|
|||
967 | static = os.path.join(templater.templatepath(), "static") |
|
|||
968 | fname = req.form['static'][0] |
|
|||
969 | req.write(staticfile(static, fname) |
|
|||
970 | or tmpl("error", error="%r not found" % fname)) |
|
|||
971 | else: |
|
|||
972 | sortable = ["name", "description", "contact", "lastchange"] |
|
|||
973 | sortcolumn, descending = self.repos_sorted |
|
|||
974 | if req.form.has_key('sort'): |
|
|||
975 | sortcolumn = req.form['sort'][0] |
|
|||
976 | descending = sortcolumn.startswith('-') |
|
|||
977 | if descending: |
|
|||
978 | sortcolumn = sortcolumn[1:] |
|
|||
979 | if sortcolumn not in sortable: |
|
|||
980 | sortcolumn = "" |
|
|||
981 |
|
||||
982 | sort = [("sort_%s" % column, |
|
|||
983 | "%s%s" % ((not descending and column == sortcolumn) |
|
|||
984 | and "-" or "", column)) |
|
|||
985 | for column in sortable] |
|
|||
986 | req.write(tmpl("index", entries=entries, |
|
|||
987 | sortcolumn=sortcolumn, descending=descending, |
|
|||
988 | **dict(sort))) |
|
This diff has been collapsed as it changes many lines, (842 lines changed) Show them Hide them | |||||
@@ -6,849 +6,13 b'' | |||||
6 | # This software may be used and distributed according to the terms |
|
6 | # This software may be used and distributed according to the terms | |
7 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | # of the GNU General Public License, incorporated herein by reference. | |
8 |
|
8 | |||
9 |
import os |
|
9 | import os | |
10 | import mimetypes |
|
|||
11 | from mercurial.demandload import demandload |
|
10 | from mercurial.demandload import demandload | |
12 |
demandload(globals(), " |
|
11 | demandload(globals(), "ConfigParser") | |
13 |
demandload(globals(), "mercurial: |
|
12 | demandload(globals(), "mercurial:ui,hg,util,templater") | |
14 | demandload(globals(), "mercurial.hgweb.request:hgrequest") |
|
13 | demandload(globals(), "mercurial.hgweb.request:hgrequest") | |
15 | demandload(globals(), "mercurial.hgweb.server:create_server") |
|
|||
16 | from mercurial.node import * |
|
|||
17 | from mercurial.i18n import gettext as _ |
|
14 | from mercurial.i18n import gettext as _ | |
18 |
|
15 | |||
19 | def up(p): |
|
|||
20 | if p[0] != "/": |
|
|||
21 | p = "/" + p |
|
|||
22 | if p[-1] == "/": |
|
|||
23 | p = p[:-1] |
|
|||
24 | up = os.path.dirname(p) |
|
|||
25 | if up == "/": |
|
|||
26 | return "/" |
|
|||
27 | return up + "/" |
|
|||
28 |
|
||||
29 | def get_mtime(repo_path): |
|
|||
30 | hg_path = os.path.join(repo_path, ".hg") |
|
|||
31 | cl_path = os.path.join(hg_path, "00changelog.i") |
|
|||
32 | if os.path.exists(os.path.join(cl_path)): |
|
|||
33 | return os.stat(cl_path).st_mtime |
|
|||
34 | else: |
|
|||
35 | return os.stat(hg_path).st_mtime |
|
|||
36 |
|
||||
37 | def staticfile(directory, fname): |
|
|||
38 | """return a file inside directory with guessed content-type header |
|
|||
39 |
|
||||
40 | fname always uses '/' as directory separator and isn't allowed to |
|
|||
41 | contain unusual path components. |
|
|||
42 | Content-type is guessed using the mimetypes module. |
|
|||
43 | Return an empty string if fname is illegal or file not found. |
|
|||
44 |
|
||||
45 | """ |
|
|||
46 | parts = fname.split('/') |
|
|||
47 | path = directory |
|
|||
48 | for part in parts: |
|
|||
49 | if (part in ('', os.curdir, os.pardir) or |
|
|||
50 | os.sep in part or os.altsep is not None and os.altsep in part): |
|
|||
51 | return "" |
|
|||
52 | path = os.path.join(path, part) |
|
|||
53 | try: |
|
|||
54 | os.stat(path) |
|
|||
55 | ct = mimetypes.guess_type(path)[0] or "text/plain" |
|
|||
56 | return "Content-type: %s\n\n%s" % (ct, file(path).read()) |
|
|||
57 | except (TypeError, OSError): |
|
|||
58 | # illegal fname or unreadable file |
|
|||
59 | return "" |
|
|||
60 |
|
||||
61 | class hgweb(object): |
|
|||
62 | def __init__(self, repo, name=None): |
|
|||
63 | if type(repo) == type(""): |
|
|||
64 | self.repo = hg.repository(ui.ui(), repo) |
|
|||
65 | else: |
|
|||
66 | self.repo = repo |
|
|||
67 |
|
||||
68 | self.mtime = -1 |
|
|||
69 | self.reponame = name |
|
|||
70 | self.archives = 'zip', 'gz', 'bz2' |
|
|||
71 |
|
||||
72 | def refresh(self): |
|
|||
73 | mtime = get_mtime(self.repo.root) |
|
|||
74 | if mtime != self.mtime: |
|
|||
75 | self.mtime = mtime |
|
|||
76 | self.repo = hg.repository(self.repo.ui, self.repo.root) |
|
|||
77 | self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10)) |
|
|||
78 | self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10)) |
|
|||
79 | self.allowpull = self.repo.ui.configbool("web", "allowpull", True) |
|
|||
80 |
|
||||
81 | def archivelist(self, nodeid): |
|
|||
82 | for i in self.archives: |
|
|||
83 | if self.repo.ui.configbool("web", "allow" + i, False): |
|
|||
84 | yield {"type" : i, "node" : nodeid, "url": ""} |
|
|||
85 |
|
||||
86 | def listfiles(self, files, mf): |
|
|||
87 | for f in files[:self.maxfiles]: |
|
|||
88 | yield self.t("filenodelink", node=hex(mf[f]), file=f) |
|
|||
89 | if len(files) > self.maxfiles: |
|
|||
90 | yield self.t("fileellipses") |
|
|||
91 |
|
||||
92 | def listfilediffs(self, files, changeset): |
|
|||
93 | for f in files[:self.maxfiles]: |
|
|||
94 | yield self.t("filedifflink", node=hex(changeset), file=f) |
|
|||
95 | if len(files) > self.maxfiles: |
|
|||
96 | yield self.t("fileellipses") |
|
|||
97 |
|
||||
98 | def siblings(self, siblings=[], rev=None, hiderev=None, **args): |
|
|||
99 | if not rev: |
|
|||
100 | rev = lambda x: "" |
|
|||
101 | siblings = [s for s in siblings if s != nullid] |
|
|||
102 | if len(siblings) == 1 and rev(siblings[0]) == hiderev: |
|
|||
103 | return |
|
|||
104 | for s in siblings: |
|
|||
105 | yield dict(node=hex(s), rev=rev(s), **args) |
|
|||
106 |
|
||||
107 | def renamelink(self, fl, node): |
|
|||
108 | r = fl.renamed(node) |
|
|||
109 | if r: |
|
|||
110 | return [dict(file=r[0], node=hex(r[1]))] |
|
|||
111 | return [] |
|
|||
112 |
|
||||
113 | def showtag(self, t1, node=nullid, **args): |
|
|||
114 | for t in self.repo.nodetags(node): |
|
|||
115 | yield self.t(t1, tag=t, **args) |
|
|||
116 |
|
||||
117 | def diff(self, node1, node2, files): |
|
|||
118 | def filterfiles(filters, files): |
|
|||
119 | l = [x for x in files if x in filters] |
|
|||
120 |
|
||||
121 | for t in filters: |
|
|||
122 | if t and t[-1] != os.sep: |
|
|||
123 | t += os.sep |
|
|||
124 | l += [x for x in files if x.startswith(t)] |
|
|||
125 | return l |
|
|||
126 |
|
||||
127 | parity = [0] |
|
|||
128 | def diffblock(diff, f, fn): |
|
|||
129 | yield self.t("diffblock", |
|
|||
130 | lines=prettyprintlines(diff), |
|
|||
131 | parity=parity[0], |
|
|||
132 | file=f, |
|
|||
133 | filenode=hex(fn or nullid)) |
|
|||
134 | parity[0] = 1 - parity[0] |
|
|||
135 |
|
||||
136 | def prettyprintlines(diff): |
|
|||
137 | for l in diff.splitlines(1): |
|
|||
138 | if l.startswith('+'): |
|
|||
139 | yield self.t("difflineplus", line=l) |
|
|||
140 | elif l.startswith('-'): |
|
|||
141 | yield self.t("difflineminus", line=l) |
|
|||
142 | elif l.startswith('@'): |
|
|||
143 | yield self.t("difflineat", line=l) |
|
|||
144 | else: |
|
|||
145 | yield self.t("diffline", line=l) |
|
|||
146 |
|
||||
147 | r = self.repo |
|
|||
148 | cl = r.changelog |
|
|||
149 | mf = r.manifest |
|
|||
150 | change1 = cl.read(node1) |
|
|||
151 | change2 = cl.read(node2) |
|
|||
152 | mmap1 = mf.read(change1[0]) |
|
|||
153 | mmap2 = mf.read(change2[0]) |
|
|||
154 | date1 = util.datestr(change1[2]) |
|
|||
155 | date2 = util.datestr(change2[2]) |
|
|||
156 |
|
||||
157 | modified, added, removed, deleted, unknown = r.changes(node1, node2) |
|
|||
158 | if files: |
|
|||
159 | modified, added, removed = map(lambda x: filterfiles(files, x), |
|
|||
160 | (modified, added, removed)) |
|
|||
161 |
|
||||
162 | diffopts = self.repo.ui.diffopts() |
|
|||
163 | showfunc = diffopts['showfunc'] |
|
|||
164 | ignorews = diffopts['ignorews'] |
|
|||
165 | for f in modified: |
|
|||
166 | to = r.file(f).read(mmap1[f]) |
|
|||
167 | tn = r.file(f).read(mmap2[f]) |
|
|||
168 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
169 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
170 | for f in added: |
|
|||
171 | to = None |
|
|||
172 | tn = r.file(f).read(mmap2[f]) |
|
|||
173 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
174 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
175 | for f in removed: |
|
|||
176 | to = r.file(f).read(mmap1[f]) |
|
|||
177 | tn = None |
|
|||
178 | yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
|
|||
179 | showfunc=showfunc, ignorews=ignorews), f, tn) |
|
|||
180 |
|
||||
181 | def changelog(self, pos): |
|
|||
182 | def changenav(**map): |
|
|||
183 | def seq(factor, maxchanges=None): |
|
|||
184 | if maxchanges: |
|
|||
185 | yield maxchanges |
|
|||
186 | if maxchanges >= 20 and maxchanges <= 40: |
|
|||
187 | yield 50 |
|
|||
188 | else: |
|
|||
189 | yield 1 * factor |
|
|||
190 | yield 3 * factor |
|
|||
191 | for f in seq(factor * 10): |
|
|||
192 | yield f |
|
|||
193 |
|
||||
194 | l = [] |
|
|||
195 | last = 0 |
|
|||
196 | for f in seq(1, self.maxchanges): |
|
|||
197 | if f < self.maxchanges or f <= last: |
|
|||
198 | continue |
|
|||
199 | if f > count: |
|
|||
200 | break |
|
|||
201 | last = f |
|
|||
202 | r = "%d" % f |
|
|||
203 | if pos + f < count: |
|
|||
204 | l.append(("+" + r, pos + f)) |
|
|||
205 | if pos - f >= 0: |
|
|||
206 | l.insert(0, ("-" + r, pos - f)) |
|
|||
207 |
|
||||
208 | yield {"rev": 0, "label": "(0)"} |
|
|||
209 |
|
||||
210 | for label, rev in l: |
|
|||
211 | yield {"label": label, "rev": rev} |
|
|||
212 |
|
||||
213 | yield {"label": "tip", "rev": "tip"} |
|
|||
214 |
|
||||
215 | def changelist(**map): |
|
|||
216 | parity = (start - end) & 1 |
|
|||
217 | cl = self.repo.changelog |
|
|||
218 | l = [] # build a list in forward order for efficiency |
|
|||
219 | for i in range(start, end): |
|
|||
220 | n = cl.node(i) |
|
|||
221 | changes = cl.read(n) |
|
|||
222 | hn = hex(n) |
|
|||
223 |
|
||||
224 | l.insert(0, {"parity": parity, |
|
|||
225 | "author": changes[1], |
|
|||
226 | "parent": self.siblings(cl.parents(n), cl.rev, |
|
|||
227 | cl.rev(n) - 1), |
|
|||
228 | "child": self.siblings(cl.children(n), cl.rev, |
|
|||
229 | cl.rev(n) + 1), |
|
|||
230 | "changelogtag": self.showtag("changelogtag",n), |
|
|||
231 | "manifest": hex(changes[0]), |
|
|||
232 | "desc": changes[4], |
|
|||
233 | "date": changes[2], |
|
|||
234 | "files": self.listfilediffs(changes[3], n), |
|
|||
235 | "rev": i, |
|
|||
236 | "node": hn}) |
|
|||
237 | parity = 1 - parity |
|
|||
238 |
|
||||
239 | for e in l: |
|
|||
240 | yield e |
|
|||
241 |
|
||||
242 | cl = self.repo.changelog |
|
|||
243 | mf = cl.read(cl.tip())[0] |
|
|||
244 | count = cl.count() |
|
|||
245 | start = max(0, pos - self.maxchanges + 1) |
|
|||
246 | end = min(count, start + self.maxchanges) |
|
|||
247 | pos = end - 1 |
|
|||
248 |
|
||||
249 | yield self.t('changelog', |
|
|||
250 | changenav=changenav, |
|
|||
251 | manifest=hex(mf), |
|
|||
252 | rev=pos, changesets=count, entries=changelist, |
|
|||
253 | archives=self.archivelist("tip")) |
|
|||
254 |
|
||||
255 | def search(self, query): |
|
|||
256 |
|
||||
257 | def changelist(**map): |
|
|||
258 | cl = self.repo.changelog |
|
|||
259 | count = 0 |
|
|||
260 | qw = query.lower().split() |
|
|||
261 |
|
||||
262 | def revgen(): |
|
|||
263 | for i in range(cl.count() - 1, 0, -100): |
|
|||
264 | l = [] |
|
|||
265 | for j in range(max(0, i - 100), i): |
|
|||
266 | n = cl.node(j) |
|
|||
267 | changes = cl.read(n) |
|
|||
268 | l.append((n, j, changes)) |
|
|||
269 | l.reverse() |
|
|||
270 | for e in l: |
|
|||
271 | yield e |
|
|||
272 |
|
||||
273 | for n, i, changes in revgen(): |
|
|||
274 | miss = 0 |
|
|||
275 | for q in qw: |
|
|||
276 | if not (q in changes[1].lower() or |
|
|||
277 | q in changes[4].lower() or |
|
|||
278 | q in " ".join(changes[3][:20]).lower()): |
|
|||
279 | miss = 1 |
|
|||
280 | break |
|
|||
281 | if miss: |
|
|||
282 | continue |
|
|||
283 |
|
||||
284 | count += 1 |
|
|||
285 | hn = hex(n) |
|
|||
286 |
|
||||
287 | yield self.t('searchentry', |
|
|||
288 | parity=count & 1, |
|
|||
289 | author=changes[1], |
|
|||
290 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
291 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
292 | changelogtag=self.showtag("changelogtag",n), |
|
|||
293 | manifest=hex(changes[0]), |
|
|||
294 | desc=changes[4], |
|
|||
295 | date=changes[2], |
|
|||
296 | files=self.listfilediffs(changes[3], n), |
|
|||
297 | rev=i, |
|
|||
298 | node=hn) |
|
|||
299 |
|
||||
300 | if count >= self.maxchanges: |
|
|||
301 | break |
|
|||
302 |
|
||||
303 | cl = self.repo.changelog |
|
|||
304 | mf = cl.read(cl.tip())[0] |
|
|||
305 |
|
||||
306 | yield self.t('search', |
|
|||
307 | query=query, |
|
|||
308 | manifest=hex(mf), |
|
|||
309 | entries=changelist) |
|
|||
310 |
|
||||
311 | def changeset(self, nodeid): |
|
|||
312 | cl = self.repo.changelog |
|
|||
313 | n = self.repo.lookup(nodeid) |
|
|||
314 | nodeid = hex(n) |
|
|||
315 | changes = cl.read(n) |
|
|||
316 | p1 = cl.parents(n)[0] |
|
|||
317 |
|
||||
318 | files = [] |
|
|||
319 | mf = self.repo.manifest.read(changes[0]) |
|
|||
320 | for f in changes[3]: |
|
|||
321 | files.append(self.t("filenodelink", |
|
|||
322 | filenode=hex(mf.get(f, nullid)), file=f)) |
|
|||
323 |
|
||||
324 | def diff(**map): |
|
|||
325 | yield self.diff(p1, n, None) |
|
|||
326 |
|
||||
327 | yield self.t('changeset', |
|
|||
328 | diff=diff, |
|
|||
329 | rev=cl.rev(n), |
|
|||
330 | node=nodeid, |
|
|||
331 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
332 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
333 | changesettag=self.showtag("changesettag",n), |
|
|||
334 | manifest=hex(changes[0]), |
|
|||
335 | author=changes[1], |
|
|||
336 | desc=changes[4], |
|
|||
337 | date=changes[2], |
|
|||
338 | files=files, |
|
|||
339 | archives=self.archivelist(nodeid)) |
|
|||
340 |
|
||||
341 | def filelog(self, f, filenode): |
|
|||
342 | cl = self.repo.changelog |
|
|||
343 | fl = self.repo.file(f) |
|
|||
344 | filenode = hex(fl.lookup(filenode)) |
|
|||
345 | count = fl.count() |
|
|||
346 |
|
||||
347 | def entries(**map): |
|
|||
348 | l = [] |
|
|||
349 | parity = (count - 1) & 1 |
|
|||
350 |
|
||||
351 | for i in range(count): |
|
|||
352 | n = fl.node(i) |
|
|||
353 | lr = fl.linkrev(n) |
|
|||
354 | cn = cl.node(lr) |
|
|||
355 | cs = cl.read(cl.node(lr)) |
|
|||
356 |
|
||||
357 | l.insert(0, {"parity": parity, |
|
|||
358 | "filenode": hex(n), |
|
|||
359 | "filerev": i, |
|
|||
360 | "file": f, |
|
|||
361 | "node": hex(cn), |
|
|||
362 | "author": cs[1], |
|
|||
363 | "date": cs[2], |
|
|||
364 | "rename": self.renamelink(fl, n), |
|
|||
365 | "parent": self.siblings(fl.parents(n), |
|
|||
366 | fl.rev, file=f), |
|
|||
367 | "child": self.siblings(fl.children(n), |
|
|||
368 | fl.rev, file=f), |
|
|||
369 | "desc": cs[4]}) |
|
|||
370 | parity = 1 - parity |
|
|||
371 |
|
||||
372 | for e in l: |
|
|||
373 | yield e |
|
|||
374 |
|
||||
375 | yield self.t("filelog", file=f, filenode=filenode, entries=entries) |
|
|||
376 |
|
||||
377 | def filerevision(self, f, node): |
|
|||
378 | fl = self.repo.file(f) |
|
|||
379 | n = fl.lookup(node) |
|
|||
380 | node = hex(n) |
|
|||
381 | text = fl.read(n) |
|
|||
382 | changerev = fl.linkrev(n) |
|
|||
383 | cl = self.repo.changelog |
|
|||
384 | cn = cl.node(changerev) |
|
|||
385 | cs = cl.read(cn) |
|
|||
386 | mfn = cs[0] |
|
|||
387 |
|
||||
388 | mt = mimetypes.guess_type(f)[0] |
|
|||
389 | rawtext = text |
|
|||
390 | if util.binary(text): |
|
|||
391 | mt = mt or 'application/octet-stream' |
|
|||
392 | text = "(binary:%s)" % mt |
|
|||
393 | mt = mt or 'text/plain' |
|
|||
394 |
|
||||
395 | def lines(): |
|
|||
396 | for l, t in enumerate(text.splitlines(1)): |
|
|||
397 | yield {"line": t, |
|
|||
398 | "linenumber": "% 6d" % (l + 1), |
|
|||
399 | "parity": l & 1} |
|
|||
400 |
|
||||
401 | yield self.t("filerevision", |
|
|||
402 | file=f, |
|
|||
403 | filenode=node, |
|
|||
404 | path=up(f), |
|
|||
405 | text=lines(), |
|
|||
406 | raw=rawtext, |
|
|||
407 | mimetype=mt, |
|
|||
408 | rev=changerev, |
|
|||
409 | node=hex(cn), |
|
|||
410 | manifest=hex(mfn), |
|
|||
411 | author=cs[1], |
|
|||
412 | date=cs[2], |
|
|||
413 | parent=self.siblings(fl.parents(n), fl.rev, file=f), |
|
|||
414 | child=self.siblings(fl.children(n), fl.rev, file=f), |
|
|||
415 | rename=self.renamelink(fl, n), |
|
|||
416 | permissions=self.repo.manifest.readflags(mfn)[f]) |
|
|||
417 |
|
||||
418 | def fileannotate(self, f, node): |
|
|||
419 | bcache = {} |
|
|||
420 | ncache = {} |
|
|||
421 | fl = self.repo.file(f) |
|
|||
422 | n = fl.lookup(node) |
|
|||
423 | node = hex(n) |
|
|||
424 | changerev = fl.linkrev(n) |
|
|||
425 |
|
||||
426 | cl = self.repo.changelog |
|
|||
427 | cn = cl.node(changerev) |
|
|||
428 | cs = cl.read(cn) |
|
|||
429 | mfn = cs[0] |
|
|||
430 |
|
||||
431 | def annotate(**map): |
|
|||
432 | parity = 1 |
|
|||
433 | last = None |
|
|||
434 | for r, l in fl.annotate(n): |
|
|||
435 | try: |
|
|||
436 | cnode = ncache[r] |
|
|||
437 | except KeyError: |
|
|||
438 | cnode = ncache[r] = self.repo.changelog.node(r) |
|
|||
439 |
|
||||
440 | try: |
|
|||
441 | name = bcache[r] |
|
|||
442 | except KeyError: |
|
|||
443 | cl = self.repo.changelog.read(cnode) |
|
|||
444 | bcache[r] = name = self.repo.ui.shortuser(cl[1]) |
|
|||
445 |
|
||||
446 | if last != cnode: |
|
|||
447 | parity = 1 - parity |
|
|||
448 | last = cnode |
|
|||
449 |
|
||||
450 | yield {"parity": parity, |
|
|||
451 | "node": hex(cnode), |
|
|||
452 | "rev": r, |
|
|||
453 | "author": name, |
|
|||
454 | "file": f, |
|
|||
455 | "line": l} |
|
|||
456 |
|
||||
457 | yield self.t("fileannotate", |
|
|||
458 | file=f, |
|
|||
459 | filenode=node, |
|
|||
460 | annotate=annotate, |
|
|||
461 | path=up(f), |
|
|||
462 | rev=changerev, |
|
|||
463 | node=hex(cn), |
|
|||
464 | manifest=hex(mfn), |
|
|||
465 | author=cs[1], |
|
|||
466 | date=cs[2], |
|
|||
467 | rename=self.renamelink(fl, n), |
|
|||
468 | parent=self.siblings(fl.parents(n), fl.rev, file=f), |
|
|||
469 | child=self.siblings(fl.children(n), fl.rev, file=f), |
|
|||
470 | permissions=self.repo.manifest.readflags(mfn)[f]) |
|
|||
471 |
|
||||
472 | def manifest(self, mnode, path): |
|
|||
473 | man = self.repo.manifest |
|
|||
474 | mn = man.lookup(mnode) |
|
|||
475 | mnode = hex(mn) |
|
|||
476 | mf = man.read(mn) |
|
|||
477 | rev = man.rev(mn) |
|
|||
478 | changerev = man.linkrev(mn) |
|
|||
479 | node = self.repo.changelog.node(changerev) |
|
|||
480 | mff = man.readflags(mn) |
|
|||
481 |
|
||||
482 | files = {} |
|
|||
483 |
|
||||
484 | p = path[1:] |
|
|||
485 | if p and p[-1] != "/": |
|
|||
486 | p += "/" |
|
|||
487 | l = len(p) |
|
|||
488 |
|
||||
489 | for f,n in mf.items(): |
|
|||
490 | if f[:l] != p: |
|
|||
491 | continue |
|
|||
492 | remain = f[l:] |
|
|||
493 | if "/" in remain: |
|
|||
494 | short = remain[:remain.find("/") + 1] # bleah |
|
|||
495 | files[short] = (f, None) |
|
|||
496 | else: |
|
|||
497 | short = os.path.basename(remain) |
|
|||
498 | files[short] = (f, n) |
|
|||
499 |
|
||||
500 | def filelist(**map): |
|
|||
501 | parity = 0 |
|
|||
502 | fl = files.keys() |
|
|||
503 | fl.sort() |
|
|||
504 | for f in fl: |
|
|||
505 | full, fnode = files[f] |
|
|||
506 | if not fnode: |
|
|||
507 | continue |
|
|||
508 |
|
||||
509 | yield {"file": full, |
|
|||
510 | "manifest": mnode, |
|
|||
511 | "filenode": hex(fnode), |
|
|||
512 | "parity": parity, |
|
|||
513 | "basename": f, |
|
|||
514 | "permissions": mff[full]} |
|
|||
515 | parity = 1 - parity |
|
|||
516 |
|
||||
517 | def dirlist(**map): |
|
|||
518 | parity = 0 |
|
|||
519 | fl = files.keys() |
|
|||
520 | fl.sort() |
|
|||
521 | for f in fl: |
|
|||
522 | full, fnode = files[f] |
|
|||
523 | if fnode: |
|
|||
524 | continue |
|
|||
525 |
|
||||
526 | yield {"parity": parity, |
|
|||
527 | "path": os.path.join(path, f), |
|
|||
528 | "manifest": mnode, |
|
|||
529 | "basename": f[:-1]} |
|
|||
530 | parity = 1 - parity |
|
|||
531 |
|
||||
532 | yield self.t("manifest", |
|
|||
533 | manifest=mnode, |
|
|||
534 | rev=rev, |
|
|||
535 | node=hex(node), |
|
|||
536 | path=path, |
|
|||
537 | up=up(path), |
|
|||
538 | fentries=filelist, |
|
|||
539 | dentries=dirlist, |
|
|||
540 | archives=self.archivelist(hex(node))) |
|
|||
541 |
|
||||
542 | def tags(self): |
|
|||
543 | cl = self.repo.changelog |
|
|||
544 | mf = cl.read(cl.tip())[0] |
|
|||
545 |
|
||||
546 | i = self.repo.tagslist() |
|
|||
547 | i.reverse() |
|
|||
548 |
|
||||
549 | def entries(notip=False, **map): |
|
|||
550 | parity = 0 |
|
|||
551 | for k,n in i: |
|
|||
552 | if notip and k == "tip": continue |
|
|||
553 | yield {"parity": parity, |
|
|||
554 | "tag": k, |
|
|||
555 | "tagmanifest": hex(cl.read(n)[0]), |
|
|||
556 | "date": cl.read(n)[2], |
|
|||
557 | "node": hex(n)} |
|
|||
558 | parity = 1 - parity |
|
|||
559 |
|
||||
560 | yield self.t("tags", |
|
|||
561 | manifest=hex(mf), |
|
|||
562 | entries=lambda **x: entries(False, **x), |
|
|||
563 | entriesnotip=lambda **x: entries(True, **x)) |
|
|||
564 |
|
||||
565 | def summary(self): |
|
|||
566 | cl = self.repo.changelog |
|
|||
567 | mf = cl.read(cl.tip())[0] |
|
|||
568 |
|
||||
569 | i = self.repo.tagslist() |
|
|||
570 | i.reverse() |
|
|||
571 |
|
||||
572 | def tagentries(**map): |
|
|||
573 | parity = 0 |
|
|||
574 | count = 0 |
|
|||
575 | for k,n in i: |
|
|||
576 | if k == "tip": # skip tip |
|
|||
577 | continue; |
|
|||
578 |
|
||||
579 | count += 1 |
|
|||
580 | if count > 10: # limit to 10 tags |
|
|||
581 | break; |
|
|||
582 |
|
||||
583 | c = cl.read(n) |
|
|||
584 | m = c[0] |
|
|||
585 | t = c[2] |
|
|||
586 |
|
||||
587 | yield self.t("tagentry", |
|
|||
588 | parity = parity, |
|
|||
589 | tag = k, |
|
|||
590 | node = hex(n), |
|
|||
591 | date = t, |
|
|||
592 | tagmanifest = hex(m)) |
|
|||
593 | parity = 1 - parity |
|
|||
594 |
|
||||
595 | def changelist(**map): |
|
|||
596 | parity = 0 |
|
|||
597 | cl = self.repo.changelog |
|
|||
598 | l = [] # build a list in forward order for efficiency |
|
|||
599 | for i in range(start, end): |
|
|||
600 | n = cl.node(i) |
|
|||
601 | changes = cl.read(n) |
|
|||
602 | hn = hex(n) |
|
|||
603 | t = changes[2] |
|
|||
604 |
|
||||
605 | l.insert(0, self.t( |
|
|||
606 | 'shortlogentry', |
|
|||
607 | parity = parity, |
|
|||
608 | author = changes[1], |
|
|||
609 | manifest = hex(changes[0]), |
|
|||
610 | desc = changes[4], |
|
|||
611 | date = t, |
|
|||
612 | rev = i, |
|
|||
613 | node = hn)) |
|
|||
614 | parity = 1 - parity |
|
|||
615 |
|
||||
616 | yield l |
|
|||
617 |
|
||||
618 | cl = self.repo.changelog |
|
|||
619 | mf = cl.read(cl.tip())[0] |
|
|||
620 | count = cl.count() |
|
|||
621 | start = max(0, count - self.maxchanges) |
|
|||
622 | end = min(count, start + self.maxchanges) |
|
|||
623 | pos = end - 1 |
|
|||
624 |
|
||||
625 | yield self.t("summary", |
|
|||
626 | desc = self.repo.ui.config("web", "description", "unknown"), |
|
|||
627 | owner = (self.repo.ui.config("ui", "username") or # preferred |
|
|||
628 | self.repo.ui.config("web", "contact") or # deprecated |
|
|||
629 | self.repo.ui.config("web", "author", "unknown")), # also |
|
|||
630 | lastchange = (0, 0), # FIXME |
|
|||
631 | manifest = hex(mf), |
|
|||
632 | tags = tagentries, |
|
|||
633 | shortlog = changelist) |
|
|||
634 |
|
||||
635 | def filediff(self, file, changeset): |
|
|||
636 | cl = self.repo.changelog |
|
|||
637 | n = self.repo.lookup(changeset) |
|
|||
638 | changeset = hex(n) |
|
|||
639 | p1 = cl.parents(n)[0] |
|
|||
640 | cs = cl.read(n) |
|
|||
641 | mf = self.repo.manifest.read(cs[0]) |
|
|||
642 |
|
||||
643 | def diff(**map): |
|
|||
644 | yield self.diff(p1, n, [file]) |
|
|||
645 |
|
||||
646 | yield self.t("filediff", |
|
|||
647 | file=file, |
|
|||
648 | filenode=hex(mf.get(file, nullid)), |
|
|||
649 | node=changeset, |
|
|||
650 | rev=self.repo.changelog.rev(n), |
|
|||
651 | parent=self.siblings(cl.parents(n), cl.rev), |
|
|||
652 | child=self.siblings(cl.children(n), cl.rev), |
|
|||
653 | diff=diff) |
|
|||
654 |
|
||||
655 | archive_specs = { |
|
|||
656 | 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', 'x-bzip2'), |
|
|||
657 | 'gz': ('application/x-tar', 'tgz', '.tar.gz', 'x-gzip'), |
|
|||
658 | 'zip': ('application/zip', 'zip', '.zip', None), |
|
|||
659 | } |
|
|||
660 |
|
||||
661 | def archive(self, req, cnode, type): |
|
|||
662 | reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame)) |
|
|||
663 | name = "%s-%s" % (reponame, short(cnode)) |
|
|||
664 | mimetype, artype, extension, encoding = self.archive_specs[type] |
|
|||
665 | headers = [('Content-type', mimetype), |
|
|||
666 | ('Content-disposition', 'attachment; filename=%s%s' % |
|
|||
667 | (name, extension))] |
|
|||
668 | if encoding: |
|
|||
669 | headers.append(('Content-encoding', encoding)) |
|
|||
670 | req.header(headers) |
|
|||
671 | archival.archive(self.repo, req.out, cnode, artype, prefix=name) |
|
|||
672 |
|
||||
673 | # add tags to things |
|
|||
674 | # tags -> list of changesets corresponding to tags |
|
|||
675 | # find tag, changeset, file |
|
|||
676 |
|
||||
677 | def run(self, req=hgrequest()): |
|
|||
678 | def clean(path): |
|
|||
679 | p = util.normpath(path) |
|
|||
680 | if p[:2] == "..": |
|
|||
681 | raise "suspicious path" |
|
|||
682 | return p |
|
|||
683 |
|
||||
684 | def header(**map): |
|
|||
685 | yield self.t("header", **map) |
|
|||
686 |
|
||||
687 | def footer(**map): |
|
|||
688 | yield self.t("footer", |
|
|||
689 | motd=self.repo.ui.config("web", "motd", ""), |
|
|||
690 | **map) |
|
|||
691 |
|
||||
692 | def expand_form(form): |
|
|||
693 | shortcuts = { |
|
|||
694 | 'cl': [('cmd', ['changelog']), ('rev', None)], |
|
|||
695 | 'cs': [('cmd', ['changeset']), ('node', None)], |
|
|||
696 | 'f': [('cmd', ['file']), ('filenode', None)], |
|
|||
697 | 'fl': [('cmd', ['filelog']), ('filenode', None)], |
|
|||
698 | 'fd': [('cmd', ['filediff']), ('node', None)], |
|
|||
699 | 'fa': [('cmd', ['annotate']), ('filenode', None)], |
|
|||
700 | 'mf': [('cmd', ['manifest']), ('manifest', None)], |
|
|||
701 | 'ca': [('cmd', ['archive']), ('node', None)], |
|
|||
702 | 'tags': [('cmd', ['tags'])], |
|
|||
703 | 'tip': [('cmd', ['changeset']), ('node', ['tip'])], |
|
|||
704 | 'static': [('cmd', ['static']), ('file', None)] |
|
|||
705 | } |
|
|||
706 |
|
||||
707 | for k in shortcuts.iterkeys(): |
|
|||
708 | if form.has_key(k): |
|
|||
709 | for name, value in shortcuts[k]: |
|
|||
710 | if value is None: |
|
|||
711 | value = form[k] |
|
|||
712 | form[name] = value |
|
|||
713 | del form[k] |
|
|||
714 |
|
||||
715 | self.refresh() |
|
|||
716 |
|
||||
717 | expand_form(req.form) |
|
|||
718 |
|
||||
719 | t = self.repo.ui.config("web", "templates", templater.templatepath()) |
|
|||
720 | static = self.repo.ui.config("web", "static", os.path.join(t,"static")) |
|
|||
721 | m = os.path.join(t, "map") |
|
|||
722 | style = self.repo.ui.config("web", "style", "") |
|
|||
723 | if req.form.has_key('style'): |
|
|||
724 | style = req.form['style'][0] |
|
|||
725 | if style: |
|
|||
726 | b = os.path.basename("map-" + style) |
|
|||
727 | p = os.path.join(t, b) |
|
|||
728 | if os.path.isfile(p): |
|
|||
729 | m = p |
|
|||
730 |
|
||||
731 | port = req.env["SERVER_PORT"] |
|
|||
732 | port = port != "80" and (":" + port) or "" |
|
|||
733 | uri = req.env["REQUEST_URI"] |
|
|||
734 | if "?" in uri: |
|
|||
735 | uri = uri.split("?")[0] |
|
|||
736 | url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri) |
|
|||
737 | if not self.reponame: |
|
|||
738 | self.reponame = (self.repo.ui.config("web", "name") |
|
|||
739 | or uri.strip('/') or self.repo.root) |
|
|||
740 |
|
||||
741 | self.t = templater.templater(m, templater.common_filters, |
|
|||
742 | defaults={"url": url, |
|
|||
743 | "repo": self.reponame, |
|
|||
744 | "header": header, |
|
|||
745 | "footer": footer, |
|
|||
746 | }) |
|
|||
747 |
|
||||
748 | if not req.form.has_key('cmd'): |
|
|||
749 | req.form['cmd'] = [self.t.cache['default'],] |
|
|||
750 |
|
||||
751 | cmd = req.form['cmd'][0] |
|
|||
752 | if cmd == 'changelog': |
|
|||
753 | hi = self.repo.changelog.count() - 1 |
|
|||
754 | if req.form.has_key('rev'): |
|
|||
755 | hi = req.form['rev'][0] |
|
|||
756 | try: |
|
|||
757 | hi = self.repo.changelog.rev(self.repo.lookup(hi)) |
|
|||
758 | except hg.RepoError: |
|
|||
759 | req.write(self.search(hi)) # XXX redirect to 404 page? |
|
|||
760 | return |
|
|||
761 |
|
||||
762 | req.write(self.changelog(hi)) |
|
|||
763 |
|
||||
764 | elif cmd == 'changeset': |
|
|||
765 | req.write(self.changeset(req.form['node'][0])) |
|
|||
766 |
|
||||
767 | elif cmd == 'manifest': |
|
|||
768 | req.write(self.manifest(req.form['manifest'][0], |
|
|||
769 | clean(req.form['path'][0]))) |
|
|||
770 |
|
||||
771 | elif cmd == 'tags': |
|
|||
772 | req.write(self.tags()) |
|
|||
773 |
|
||||
774 | elif cmd == 'summary': |
|
|||
775 | req.write(self.summary()) |
|
|||
776 |
|
||||
777 | elif cmd == 'filediff': |
|
|||
778 | req.write(self.filediff(clean(req.form['file'][0]), |
|
|||
779 | req.form['node'][0])) |
|
|||
780 |
|
||||
781 | elif cmd == 'file': |
|
|||
782 | req.write(self.filerevision(clean(req.form['file'][0]), |
|
|||
783 | req.form['filenode'][0])) |
|
|||
784 |
|
||||
785 | elif cmd == 'annotate': |
|
|||
786 | req.write(self.fileannotate(clean(req.form['file'][0]), |
|
|||
787 | req.form['filenode'][0])) |
|
|||
788 |
|
||||
789 | elif cmd == 'filelog': |
|
|||
790 | req.write(self.filelog(clean(req.form['file'][0]), |
|
|||
791 | req.form['filenode'][0])) |
|
|||
792 |
|
||||
793 | elif cmd == 'heads': |
|
|||
794 | req.httphdr("application/mercurial-0.1") |
|
|||
795 | h = self.repo.heads() |
|
|||
796 | req.write(" ".join(map(hex, h)) + "\n") |
|
|||
797 |
|
||||
798 | elif cmd == 'branches': |
|
|||
799 | req.httphdr("application/mercurial-0.1") |
|
|||
800 | nodes = [] |
|
|||
801 | if req.form.has_key('nodes'): |
|
|||
802 | nodes = map(bin, req.form['nodes'][0].split(" ")) |
|
|||
803 | for b in self.repo.branches(nodes): |
|
|||
804 | req.write(" ".join(map(hex, b)) + "\n") |
|
|||
805 |
|
||||
806 | elif cmd == 'between': |
|
|||
807 | req.httphdr("application/mercurial-0.1") |
|
|||
808 | nodes = [] |
|
|||
809 | if req.form.has_key('pairs'): |
|
|||
810 | pairs = [map(bin, p.split("-")) |
|
|||
811 | for p in req.form['pairs'][0].split(" ")] |
|
|||
812 | for b in self.repo.between(pairs): |
|
|||
813 | req.write(" ".join(map(hex, b)) + "\n") |
|
|||
814 |
|
||||
815 | elif cmd == 'changegroup': |
|
|||
816 | req.httphdr("application/mercurial-0.1") |
|
|||
817 | nodes = [] |
|
|||
818 | if not self.allowpull: |
|
|||
819 | return |
|
|||
820 |
|
||||
821 | if req.form.has_key('roots'): |
|
|||
822 | nodes = map(bin, req.form['roots'][0].split(" ")) |
|
|||
823 |
|
||||
824 | z = zlib.compressobj() |
|
|||
825 | f = self.repo.changegroup(nodes, 'serve') |
|
|||
826 | while 1: |
|
|||
827 | chunk = f.read(4096) |
|
|||
828 | if not chunk: |
|
|||
829 | break |
|
|||
830 | req.write(z.compress(chunk)) |
|
|||
831 |
|
||||
832 | req.write(z.flush()) |
|
|||
833 |
|
||||
834 | elif cmd == 'archive': |
|
|||
835 | changeset = self.repo.lookup(req.form['node'][0]) |
|
|||
836 | type = req.form['type'][0] |
|
|||
837 | if (type in self.archives and |
|
|||
838 | self.repo.ui.configbool("web", "allow" + type, False)): |
|
|||
839 | self.archive(req, changeset, type) |
|
|||
840 | return |
|
|||
841 |
|
||||
842 | req.write(self.t("error")) |
|
|||
843 |
|
||||
844 | elif cmd == 'static': |
|
|||
845 | fname = req.form['file'][0] |
|
|||
846 | req.write(staticfile(static, fname) |
|
|||
847 | or self.t("error", error="%r not found" % fname)) |
|
|||
848 |
|
||||
849 | else: |
|
|||
850 | req.write(self.t("error")) |
|
|||
851 |
|
||||
852 | # This is a stopgap |
|
16 | # This is a stopgap | |
853 | class hgwebdir(object): |
|
17 | class hgwebdir(object): | |
854 | def __init__(self, config): |
|
18 | def __init__(self, config): |
General Comments 0
You need to be logged in to leave comments.
Login now