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