##// END OF EJS Templates
fix changenav for maxchanges != 10...
Johannes Stezenbach -
r1703:41d884f7 default
parent child Browse files
Show More
@@ -1,1139 +1,1145 b''
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, maxchanges=None):
302 yield 1 * factor
302 if maxchanges:
303 yield 3 * factor
303 yield maxchanges
304 #yield 5 * factor
304 if maxchanges >= 20 and maxchanges <= 40:
305 yield 50
306 else:
307 yield 1 * factor
308 yield 3 * factor
305 for f in seq(factor * 10):
309 for f in seq(factor * 10):
306 yield f
310 yield f
307
311
308 l = []
312 l = []
309 for f in seq():
313 last = 0
310 if f < self.maxchanges / 2:
314 for f in seq(1, self.maxchanges):
315 if f < self.maxchanges or f <= last:
311 continue
316 continue
312 if f > count:
317 if f > count:
313 break
318 break
319 last = f
314 r = "%d" % f
320 r = "%d" % f
315 if pos + f < count:
321 if pos + f < count:
316 l.append(("+" + r, pos + f))
322 l.append(("+" + r, pos + f))
317 if pos - f >= 0:
323 if pos - f >= 0:
318 l.insert(0, ("-" + r, pos - f))
324 l.insert(0, ("-" + r, pos - f))
319
325
320 yield {"rev": 0, "label": "(0)"}
326 yield {"rev": 0, "label": "(0)"}
321
327
322 for label, rev in l:
328 for label, rev in l:
323 yield {"label": label, "rev": rev}
329 yield {"label": label, "rev": rev}
324
330
325 yield {"label": "tip", "rev": "tip"}
331 yield {"label": "tip", "rev": "tip"}
326
332
327 def changelist(**map):
333 def changelist(**map):
328 parity = (start - end) & 1
334 parity = (start - end) & 1
329 cl = self.repo.changelog
335 cl = self.repo.changelog
330 l = [] # build a list in forward order for efficiency
336 l = [] # build a list in forward order for efficiency
331 for i in range(start, end):
337 for i in range(start, end):
332 n = cl.node(i)
338 n = cl.node(i)
333 changes = cl.read(n)
339 changes = cl.read(n)
334 hn = hex(n)
340 hn = hex(n)
335
341
336 l.insert(0, {"parity": parity,
342 l.insert(0, {"parity": parity,
337 "author": changes[1],
343 "author": changes[1],
338 "parent": self.siblings(cl.parents(n), cl.rev,
344 "parent": self.siblings(cl.parents(n), cl.rev,
339 cl.rev(n) - 1),
345 cl.rev(n) - 1),
340 "child": self.siblings(cl.children(n), cl.rev,
346 "child": self.siblings(cl.children(n), cl.rev,
341 cl.rev(n) + 1),
347 cl.rev(n) + 1),
342 "changelogtag": self.showtag("changelogtag",n),
348 "changelogtag": self.showtag("changelogtag",n),
343 "manifest": hex(changes[0]),
349 "manifest": hex(changes[0]),
344 "desc": changes[4],
350 "desc": changes[4],
345 "date": changes[2],
351 "date": changes[2],
346 "files": self.listfilediffs(changes[3], n),
352 "files": self.listfilediffs(changes[3], n),
347 "rev": i,
353 "rev": i,
348 "node": hn})
354 "node": hn})
349 parity = 1 - parity
355 parity = 1 - parity
350
356
351 for e in l:
357 for e in l:
352 yield e
358 yield e
353
359
354 cl = self.repo.changelog
360 cl = self.repo.changelog
355 mf = cl.read(cl.tip())[0]
361 mf = cl.read(cl.tip())[0]
356 count = cl.count()
362 count = cl.count()
357 start = max(0, pos - self.maxchanges + 1)
363 start = max(0, pos - self.maxchanges + 1)
358 end = min(count, start + self.maxchanges)
364 end = min(count, start + self.maxchanges)
359 pos = end - 1
365 pos = end - 1
360
366
361 yield self.t('changelog',
367 yield self.t('changelog',
362 changenav=changenav,
368 changenav=changenav,
363 manifest=hex(mf),
369 manifest=hex(mf),
364 rev=pos, changesets=count, entries=changelist)
370 rev=pos, changesets=count, entries=changelist)
365
371
366 def search(self, query):
372 def search(self, query):
367
373
368 def changelist(**map):
374 def changelist(**map):
369 cl = self.repo.changelog
375 cl = self.repo.changelog
370 count = 0
376 count = 0
371 qw = query.lower().split()
377 qw = query.lower().split()
372
378
373 def revgen():
379 def revgen():
374 for i in range(cl.count() - 1, 0, -100):
380 for i in range(cl.count() - 1, 0, -100):
375 l = []
381 l = []
376 for j in range(max(0, i - 100), i):
382 for j in range(max(0, i - 100), i):
377 n = cl.node(j)
383 n = cl.node(j)
378 changes = cl.read(n)
384 changes = cl.read(n)
379 l.append((n, j, changes))
385 l.append((n, j, changes))
380 l.reverse()
386 l.reverse()
381 for e in l:
387 for e in l:
382 yield e
388 yield e
383
389
384 for n, i, changes in revgen():
390 for n, i, changes in revgen():
385 miss = 0
391 miss = 0
386 for q in qw:
392 for q in qw:
387 if not (q in changes[1].lower() or
393 if not (q in changes[1].lower() or
388 q in changes[4].lower() or
394 q in changes[4].lower() or
389 q in " ".join(changes[3][:20]).lower()):
395 q in " ".join(changes[3][:20]).lower()):
390 miss = 1
396 miss = 1
391 break
397 break
392 if miss:
398 if miss:
393 continue
399 continue
394
400
395 count += 1
401 count += 1
396 hn = hex(n)
402 hn = hex(n)
397
403
398 yield self.t('searchentry',
404 yield self.t('searchentry',
399 parity=count & 1,
405 parity=count & 1,
400 author=changes[1],
406 author=changes[1],
401 parent=self.siblings(cl.parents(n), cl.rev),
407 parent=self.siblings(cl.parents(n), cl.rev),
402 child=self.siblings(cl.children(n), cl.rev),
408 child=self.siblings(cl.children(n), cl.rev),
403 changelogtag=self.showtag("changelogtag",n),
409 changelogtag=self.showtag("changelogtag",n),
404 manifest=hex(changes[0]),
410 manifest=hex(changes[0]),
405 desc=changes[4],
411 desc=changes[4],
406 date=changes[2],
412 date=changes[2],
407 files=self.listfilediffs(changes[3], n),
413 files=self.listfilediffs(changes[3], n),
408 rev=i,
414 rev=i,
409 node=hn)
415 node=hn)
410
416
411 if count >= self.maxchanges:
417 if count >= self.maxchanges:
412 break
418 break
413
419
414 cl = self.repo.changelog
420 cl = self.repo.changelog
415 mf = cl.read(cl.tip())[0]
421 mf = cl.read(cl.tip())[0]
416
422
417 yield self.t('search',
423 yield self.t('search',
418 query=query,
424 query=query,
419 manifest=hex(mf),
425 manifest=hex(mf),
420 entries=changelist)
426 entries=changelist)
421
427
422 def changeset(self, nodeid):
428 def changeset(self, nodeid):
423 cl = self.repo.changelog
429 cl = self.repo.changelog
424 n = self.repo.lookup(nodeid)
430 n = self.repo.lookup(nodeid)
425 nodeid = hex(n)
431 nodeid = hex(n)
426 changes = cl.read(n)
432 changes = cl.read(n)
427 p1 = cl.parents(n)[0]
433 p1 = cl.parents(n)[0]
428
434
429 files = []
435 files = []
430 mf = self.repo.manifest.read(changes[0])
436 mf = self.repo.manifest.read(changes[0])
431 for f in changes[3]:
437 for f in changes[3]:
432 files.append(self.t("filenodelink",
438 files.append(self.t("filenodelink",
433 filenode=hex(mf.get(f, nullid)), file=f))
439 filenode=hex(mf.get(f, nullid)), file=f))
434
440
435 def diff(**map):
441 def diff(**map):
436 yield self.diff(p1, n, None)
442 yield self.diff(p1, n, None)
437
443
438 yield self.t('changeset',
444 yield self.t('changeset',
439 diff=diff,
445 diff=diff,
440 rev=cl.rev(n),
446 rev=cl.rev(n),
441 node=nodeid,
447 node=nodeid,
442 parent=self.siblings(cl.parents(n), cl.rev),
448 parent=self.siblings(cl.parents(n), cl.rev),
443 child=self.siblings(cl.children(n), cl.rev),
449 child=self.siblings(cl.children(n), cl.rev),
444 changesettag=self.showtag("changesettag",n),
450 changesettag=self.showtag("changesettag",n),
445 manifest=hex(changes[0]),
451 manifest=hex(changes[0]),
446 author=changes[1],
452 author=changes[1],
447 desc=changes[4],
453 desc=changes[4],
448 date=changes[2],
454 date=changes[2],
449 files=files,
455 files=files,
450 archives=self.archivelist(nodeid))
456 archives=self.archivelist(nodeid))
451
457
452 def filelog(self, f, filenode):
458 def filelog(self, f, filenode):
453 cl = self.repo.changelog
459 cl = self.repo.changelog
454 fl = self.repo.file(f)
460 fl = self.repo.file(f)
455 filenode = hex(fl.lookup(filenode))
461 filenode = hex(fl.lookup(filenode))
456 count = fl.count()
462 count = fl.count()
457
463
458 def entries(**map):
464 def entries(**map):
459 l = []
465 l = []
460 parity = (count - 1) & 1
466 parity = (count - 1) & 1
461
467
462 for i in range(count):
468 for i in range(count):
463 n = fl.node(i)
469 n = fl.node(i)
464 lr = fl.linkrev(n)
470 lr = fl.linkrev(n)
465 cn = cl.node(lr)
471 cn = cl.node(lr)
466 cs = cl.read(cl.node(lr))
472 cs = cl.read(cl.node(lr))
467
473
468 l.insert(0, {"parity": parity,
474 l.insert(0, {"parity": parity,
469 "filenode": hex(n),
475 "filenode": hex(n),
470 "filerev": i,
476 "filerev": i,
471 "file": f,
477 "file": f,
472 "node": hex(cn),
478 "node": hex(cn),
473 "author": cs[1],
479 "author": cs[1],
474 "date": cs[2],
480 "date": cs[2],
475 "rename": self.renamelink(fl, n),
481 "rename": self.renamelink(fl, n),
476 "parent": self.siblings(fl.parents(n),
482 "parent": self.siblings(fl.parents(n),
477 fl.rev, file=f),
483 fl.rev, file=f),
478 "child": self.siblings(fl.children(n),
484 "child": self.siblings(fl.children(n),
479 fl.rev, file=f),
485 fl.rev, file=f),
480 "desc": cs[4]})
486 "desc": cs[4]})
481 parity = 1 - parity
487 parity = 1 - parity
482
488
483 for e in l:
489 for e in l:
484 yield e
490 yield e
485
491
486 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
492 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
487
493
488 def filerevision(self, f, node):
494 def filerevision(self, f, node):
489 fl = self.repo.file(f)
495 fl = self.repo.file(f)
490 n = fl.lookup(node)
496 n = fl.lookup(node)
491 node = hex(n)
497 node = hex(n)
492 text = fl.read(n)
498 text = fl.read(n)
493 changerev = fl.linkrev(n)
499 changerev = fl.linkrev(n)
494 cl = self.repo.changelog
500 cl = self.repo.changelog
495 cn = cl.node(changerev)
501 cn = cl.node(changerev)
496 cs = cl.read(cn)
502 cs = cl.read(cn)
497 mfn = cs[0]
503 mfn = cs[0]
498
504
499 mt = mimetypes.guess_type(f)[0]
505 mt = mimetypes.guess_type(f)[0]
500 rawtext = text
506 rawtext = text
501 if util.binary(text):
507 if util.binary(text):
502 text = "(binary:%s)" % mt
508 text = "(binary:%s)" % mt
503
509
504 def lines():
510 def lines():
505 for l, t in enumerate(text.splitlines(1)):
511 for l, t in enumerate(text.splitlines(1)):
506 yield {"line": t,
512 yield {"line": t,
507 "linenumber": "% 6d" % (l + 1),
513 "linenumber": "% 6d" % (l + 1),
508 "parity": l & 1}
514 "parity": l & 1}
509
515
510 yield self.t("filerevision",
516 yield self.t("filerevision",
511 file=f,
517 file=f,
512 filenode=node,
518 filenode=node,
513 path=up(f),
519 path=up(f),
514 text=lines(),
520 text=lines(),
515 raw=rawtext,
521 raw=rawtext,
516 mimetype=mt,
522 mimetype=mt,
517 rev=changerev,
523 rev=changerev,
518 node=hex(cn),
524 node=hex(cn),
519 manifest=hex(mfn),
525 manifest=hex(mfn),
520 author=cs[1],
526 author=cs[1],
521 date=cs[2],
527 date=cs[2],
522 parent=self.siblings(fl.parents(n), fl.rev, file=f),
528 parent=self.siblings(fl.parents(n), fl.rev, file=f),
523 child=self.siblings(fl.children(n), fl.rev, file=f),
529 child=self.siblings(fl.children(n), fl.rev, file=f),
524 rename=self.renamelink(fl, n),
530 rename=self.renamelink(fl, n),
525 permissions=self.repo.manifest.readflags(mfn)[f])
531 permissions=self.repo.manifest.readflags(mfn)[f])
526
532
527 def fileannotate(self, f, node):
533 def fileannotate(self, f, node):
528 bcache = {}
534 bcache = {}
529 ncache = {}
535 ncache = {}
530 fl = self.repo.file(f)
536 fl = self.repo.file(f)
531 n = fl.lookup(node)
537 n = fl.lookup(node)
532 node = hex(n)
538 node = hex(n)
533 changerev = fl.linkrev(n)
539 changerev = fl.linkrev(n)
534
540
535 cl = self.repo.changelog
541 cl = self.repo.changelog
536 cn = cl.node(changerev)
542 cn = cl.node(changerev)
537 cs = cl.read(cn)
543 cs = cl.read(cn)
538 mfn = cs[0]
544 mfn = cs[0]
539
545
540 def annotate(**map):
546 def annotate(**map):
541 parity = 1
547 parity = 1
542 last = None
548 last = None
543 for r, l in fl.annotate(n):
549 for r, l in fl.annotate(n):
544 try:
550 try:
545 cnode = ncache[r]
551 cnode = ncache[r]
546 except KeyError:
552 except KeyError:
547 cnode = ncache[r] = self.repo.changelog.node(r)
553 cnode = ncache[r] = self.repo.changelog.node(r)
548
554
549 try:
555 try:
550 name = bcache[r]
556 name = bcache[r]
551 except KeyError:
557 except KeyError:
552 cl = self.repo.changelog.read(cnode)
558 cl = self.repo.changelog.read(cnode)
553 bcache[r] = name = self.repo.ui.shortuser(cl[1])
559 bcache[r] = name = self.repo.ui.shortuser(cl[1])
554
560
555 if last != cnode:
561 if last != cnode:
556 parity = 1 - parity
562 parity = 1 - parity
557 last = cnode
563 last = cnode
558
564
559 yield {"parity": parity,
565 yield {"parity": parity,
560 "node": hex(cnode),
566 "node": hex(cnode),
561 "rev": r,
567 "rev": r,
562 "author": name,
568 "author": name,
563 "file": f,
569 "file": f,
564 "line": l}
570 "line": l}
565
571
566 yield self.t("fileannotate",
572 yield self.t("fileannotate",
567 file=f,
573 file=f,
568 filenode=node,
574 filenode=node,
569 annotate=annotate,
575 annotate=annotate,
570 path=up(f),
576 path=up(f),
571 rev=changerev,
577 rev=changerev,
572 node=hex(cn),
578 node=hex(cn),
573 manifest=hex(mfn),
579 manifest=hex(mfn),
574 author=cs[1],
580 author=cs[1],
575 date=cs[2],
581 date=cs[2],
576 rename=self.renamelink(fl, n),
582 rename=self.renamelink(fl, n),
577 parent=self.siblings(fl.parents(n), fl.rev, file=f),
583 parent=self.siblings(fl.parents(n), fl.rev, file=f),
578 child=self.siblings(fl.children(n), fl.rev, file=f),
584 child=self.siblings(fl.children(n), fl.rev, file=f),
579 permissions=self.repo.manifest.readflags(mfn)[f])
585 permissions=self.repo.manifest.readflags(mfn)[f])
580
586
581 def manifest(self, mnode, path):
587 def manifest(self, mnode, path):
582 man = self.repo.manifest
588 man = self.repo.manifest
583 mn = man.lookup(mnode)
589 mn = man.lookup(mnode)
584 mnode = hex(mn)
590 mnode = hex(mn)
585 mf = man.read(mn)
591 mf = man.read(mn)
586 rev = man.rev(mn)
592 rev = man.rev(mn)
587 node = self.repo.changelog.node(rev)
593 node = self.repo.changelog.node(rev)
588 mff = man.readflags(mn)
594 mff = man.readflags(mn)
589
595
590 files = {}
596 files = {}
591
597
592 p = path[1:]
598 p = path[1:]
593 if p and p[-1] != "/":
599 if p and p[-1] != "/":
594 p += "/"
600 p += "/"
595 l = len(p)
601 l = len(p)
596
602
597 for f,n in mf.items():
603 for f,n in mf.items():
598 if f[:l] != p:
604 if f[:l] != p:
599 continue
605 continue
600 remain = f[l:]
606 remain = f[l:]
601 if "/" in remain:
607 if "/" in remain:
602 short = remain[:remain.find("/") + 1] # bleah
608 short = remain[:remain.find("/") + 1] # bleah
603 files[short] = (f, None)
609 files[short] = (f, None)
604 else:
610 else:
605 short = os.path.basename(remain)
611 short = os.path.basename(remain)
606 files[short] = (f, n)
612 files[short] = (f, n)
607
613
608 def filelist(**map):
614 def filelist(**map):
609 parity = 0
615 parity = 0
610 fl = files.keys()
616 fl = files.keys()
611 fl.sort()
617 fl.sort()
612 for f in fl:
618 for f in fl:
613 full, fnode = files[f]
619 full, fnode = files[f]
614 if not fnode:
620 if not fnode:
615 continue
621 continue
616
622
617 yield {"file": full,
623 yield {"file": full,
618 "manifest": mnode,
624 "manifest": mnode,
619 "filenode": hex(fnode),
625 "filenode": hex(fnode),
620 "parity": parity,
626 "parity": parity,
621 "basename": f,
627 "basename": f,
622 "permissions": mff[full]}
628 "permissions": mff[full]}
623 parity = 1 - parity
629 parity = 1 - parity
624
630
625 def dirlist(**map):
631 def dirlist(**map):
626 parity = 0
632 parity = 0
627 fl = files.keys()
633 fl = files.keys()
628 fl.sort()
634 fl.sort()
629 for f in fl:
635 for f in fl:
630 full, fnode = files[f]
636 full, fnode = files[f]
631 if fnode:
637 if fnode:
632 continue
638 continue
633
639
634 yield {"parity": parity,
640 yield {"parity": parity,
635 "path": os.path.join(path, f),
641 "path": os.path.join(path, f),
636 "manifest": mnode,
642 "manifest": mnode,
637 "basename": f[:-1]}
643 "basename": f[:-1]}
638 parity = 1 - parity
644 parity = 1 - parity
639
645
640 yield self.t("manifest",
646 yield self.t("manifest",
641 manifest=mnode,
647 manifest=mnode,
642 rev=rev,
648 rev=rev,
643 node=hex(node),
649 node=hex(node),
644 path=path,
650 path=path,
645 up=up(path),
651 up=up(path),
646 fentries=filelist,
652 fentries=filelist,
647 dentries=dirlist,
653 dentries=dirlist,
648 archives=self.archivelist(hex(node)))
654 archives=self.archivelist(hex(node)))
649
655
650 def tags(self):
656 def tags(self):
651 cl = self.repo.changelog
657 cl = self.repo.changelog
652 mf = cl.read(cl.tip())[0]
658 mf = cl.read(cl.tip())[0]
653
659
654 i = self.repo.tagslist()
660 i = self.repo.tagslist()
655 i.reverse()
661 i.reverse()
656
662
657 def entries(**map):
663 def entries(**map):
658 parity = 0
664 parity = 0
659 for k,n in i:
665 for k,n in i:
660 yield {"parity": parity,
666 yield {"parity": parity,
661 "tag": k,
667 "tag": k,
662 "tagmanifest": hex(cl.read(n)[0]),
668 "tagmanifest": hex(cl.read(n)[0]),
663 "date": cl.read(n)[2],
669 "date": cl.read(n)[2],
664 "node": hex(n)}
670 "node": hex(n)}
665 parity = 1 - parity
671 parity = 1 - parity
666
672
667 yield self.t("tags",
673 yield self.t("tags",
668 manifest=hex(mf),
674 manifest=hex(mf),
669 entries=entries)
675 entries=entries)
670
676
671 def summary(self):
677 def summary(self):
672 cl = self.repo.changelog
678 cl = self.repo.changelog
673 mf = cl.read(cl.tip())[0]
679 mf = cl.read(cl.tip())[0]
674
680
675 i = self.repo.tagslist()
681 i = self.repo.tagslist()
676 i.reverse()
682 i.reverse()
677
683
678 def tagentries(**map):
684 def tagentries(**map):
679 parity = 0
685 parity = 0
680 count = 0
686 count = 0
681 for k,n in i:
687 for k,n in i:
682 if k == "tip": # skip tip
688 if k == "tip": # skip tip
683 continue;
689 continue;
684
690
685 count += 1
691 count += 1
686 if count > 10: # limit to 10 tags
692 if count > 10: # limit to 10 tags
687 break;
693 break;
688
694
689 c = cl.read(n)
695 c = cl.read(n)
690 m = c[0]
696 m = c[0]
691 t = c[2]
697 t = c[2]
692
698
693 yield self.t("tagentry",
699 yield self.t("tagentry",
694 parity = parity,
700 parity = parity,
695 tag = k,
701 tag = k,
696 node = hex(n),
702 node = hex(n),
697 date = t,
703 date = t,
698 tagmanifest = hex(m))
704 tagmanifest = hex(m))
699 parity = 1 - parity
705 parity = 1 - parity
700
706
701 def changelist(**map):
707 def changelist(**map):
702 parity = 0
708 parity = 0
703 cl = self.repo.changelog
709 cl = self.repo.changelog
704 l = [] # build a list in forward order for efficiency
710 l = [] # build a list in forward order for efficiency
705 for i in range(start, end):
711 for i in range(start, end):
706 n = cl.node(i)
712 n = cl.node(i)
707 changes = cl.read(n)
713 changes = cl.read(n)
708 hn = hex(n)
714 hn = hex(n)
709 t = changes[2]
715 t = changes[2]
710
716
711 l.insert(0, self.t(
717 l.insert(0, self.t(
712 'shortlogentry',
718 'shortlogentry',
713 parity = parity,
719 parity = parity,
714 author = changes[1],
720 author = changes[1],
715 manifest = hex(changes[0]),
721 manifest = hex(changes[0]),
716 desc = changes[4],
722 desc = changes[4],
717 date = t,
723 date = t,
718 rev = i,
724 rev = i,
719 node = hn))
725 node = hn))
720 parity = 1 - parity
726 parity = 1 - parity
721
727
722 yield l
728 yield l
723
729
724 cl = self.repo.changelog
730 cl = self.repo.changelog
725 mf = cl.read(cl.tip())[0]
731 mf = cl.read(cl.tip())[0]
726 count = cl.count()
732 count = cl.count()
727 start = max(0, count - self.maxchanges)
733 start = max(0, count - self.maxchanges)
728 end = min(count, start + self.maxchanges)
734 end = min(count, start + self.maxchanges)
729 pos = end - 1
735 pos = end - 1
730
736
731 yield self.t("summary",
737 yield self.t("summary",
732 desc = self.repo.ui.config("web", "description", "unknown"),
738 desc = self.repo.ui.config("web", "description", "unknown"),
733 owner = (self.repo.ui.config("ui", "username") or # preferred
739 owner = (self.repo.ui.config("ui", "username") or # preferred
734 self.repo.ui.config("web", "contact") or # deprecated
740 self.repo.ui.config("web", "contact") or # deprecated
735 self.repo.ui.config("web", "author", "unknown")), # also
741 self.repo.ui.config("web", "author", "unknown")), # also
736 lastchange = (0, 0), # FIXME
742 lastchange = (0, 0), # FIXME
737 manifest = hex(mf),
743 manifest = hex(mf),
738 tags = tagentries,
744 tags = tagentries,
739 shortlog = changelist)
745 shortlog = changelist)
740
746
741 def filediff(self, file, changeset):
747 def filediff(self, file, changeset):
742 cl = self.repo.changelog
748 cl = self.repo.changelog
743 n = self.repo.lookup(changeset)
749 n = self.repo.lookup(changeset)
744 changeset = hex(n)
750 changeset = hex(n)
745 p1 = cl.parents(n)[0]
751 p1 = cl.parents(n)[0]
746 cs = cl.read(n)
752 cs = cl.read(n)
747 mf = self.repo.manifest.read(cs[0])
753 mf = self.repo.manifest.read(cs[0])
748
754
749 def diff(**map):
755 def diff(**map):
750 yield self.diff(p1, n, file)
756 yield self.diff(p1, n, file)
751
757
752 yield self.t("filediff",
758 yield self.t("filediff",
753 file=file,
759 file=file,
754 filenode=hex(mf.get(file, nullid)),
760 filenode=hex(mf.get(file, nullid)),
755 node=changeset,
761 node=changeset,
756 rev=self.repo.changelog.rev(n),
762 rev=self.repo.changelog.rev(n),
757 parent=self.siblings(cl.parents(n), cl.rev),
763 parent=self.siblings(cl.parents(n), cl.rev),
758 child=self.siblings(cl.children(n), cl.rev),
764 child=self.siblings(cl.children(n), cl.rev),
759 diff=diff)
765 diff=diff)
760
766
761 def archive(self, req, cnode, type):
767 def archive(self, req, cnode, type):
762 cs = self.repo.changelog.read(cnode)
768 cs = self.repo.changelog.read(cnode)
763 mnode = cs[0]
769 mnode = cs[0]
764 mf = self.repo.manifest.read(mnode)
770 mf = self.repo.manifest.read(mnode)
765 rev = self.repo.manifest.rev(mnode)
771 rev = self.repo.manifest.rev(mnode)
766 reponame = re.sub(r"\W+", "-", self.reponame)
772 reponame = re.sub(r"\W+", "-", self.reponame)
767 name = "%s-%s/" % (reponame, short(cnode))
773 name = "%s-%s/" % (reponame, short(cnode))
768
774
769 files = mf.keys()
775 files = mf.keys()
770 files.sort()
776 files.sort()
771
777
772 if type == 'zip':
778 if type == 'zip':
773 tmp = tempfile.mkstemp()[1]
779 tmp = tempfile.mkstemp()[1]
774 try:
780 try:
775 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
781 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
776
782
777 for f in files:
783 for f in files:
778 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
784 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
779 zf.close()
785 zf.close()
780
786
781 f = open(tmp, 'r')
787 f = open(tmp, 'r')
782 req.httphdr('application/zip', name[:-1] + '.zip',
788 req.httphdr('application/zip', name[:-1] + '.zip',
783 os.path.getsize(tmp))
789 os.path.getsize(tmp))
784 req.write(f.read())
790 req.write(f.read())
785 f.close()
791 f.close()
786 finally:
792 finally:
787 os.unlink(tmp)
793 os.unlink(tmp)
788
794
789 else:
795 else:
790 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
796 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
791 mff = self.repo.manifest.readflags(mnode)
797 mff = self.repo.manifest.readflags(mnode)
792 mtime = int(time.time())
798 mtime = int(time.time())
793
799
794 if type == "gz":
800 if type == "gz":
795 encoding = "gzip"
801 encoding = "gzip"
796 else:
802 else:
797 encoding = "x-bzip2"
803 encoding = "x-bzip2"
798 req.header([('Content-type', 'application/x-tar'),
804 req.header([('Content-type', 'application/x-tar'),
799 ('Content-disposition', 'attachment; filename=%s%s%s' %
805 ('Content-disposition', 'attachment; filename=%s%s%s' %
800 (name[:-1], '.tar.', type)),
806 (name[:-1], '.tar.', type)),
801 ('Content-encoding', encoding)])
807 ('Content-encoding', encoding)])
802 for fname in files:
808 for fname in files:
803 rcont = self.repo.file(fname).read(mf[fname])
809 rcont = self.repo.file(fname).read(mf[fname])
804 finfo = tarfile.TarInfo(name + fname)
810 finfo = tarfile.TarInfo(name + fname)
805 finfo.mtime = mtime
811 finfo.mtime = mtime
806 finfo.size = len(rcont)
812 finfo.size = len(rcont)
807 finfo.mode = mff[fname] and 0755 or 0644
813 finfo.mode = mff[fname] and 0755 or 0644
808 tf.addfile(finfo, StringIO.StringIO(rcont))
814 tf.addfile(finfo, StringIO.StringIO(rcont))
809 tf.close()
815 tf.close()
810
816
811 # add tags to things
817 # add tags to things
812 # tags -> list of changesets corresponding to tags
818 # tags -> list of changesets corresponding to tags
813 # find tag, changeset, file
819 # find tag, changeset, file
814
820
815 def run(self, req=hgrequest()):
821 def run(self, req=hgrequest()):
816 def clean(path):
822 def clean(path):
817 p = os.path.normpath(path)
823 p = os.path.normpath(path)
818 if p[:2] == "..":
824 if p[:2] == "..":
819 raise "suspicious path"
825 raise "suspicious path"
820 return p
826 return p
821
827
822 def header(**map):
828 def header(**map):
823 yield self.t("header", **map)
829 yield self.t("header", **map)
824
830
825 def footer(**map):
831 def footer(**map):
826 yield self.t("footer", **map)
832 yield self.t("footer", **map)
827
833
828 def expand_form(form):
834 def expand_form(form):
829 shortcuts = {
835 shortcuts = {
830 'cl': [('cmd', ['changelog']), ('rev', None)],
836 'cl': [('cmd', ['changelog']), ('rev', None)],
831 'cs': [('cmd', ['changeset']), ('node', None)],
837 'cs': [('cmd', ['changeset']), ('node', None)],
832 'f': [('cmd', ['file']), ('filenode', None)],
838 'f': [('cmd', ['file']), ('filenode', None)],
833 'fl': [('cmd', ['filelog']), ('filenode', None)],
839 'fl': [('cmd', ['filelog']), ('filenode', None)],
834 'fd': [('cmd', ['filediff']), ('node', None)],
840 'fd': [('cmd', ['filediff']), ('node', None)],
835 'fa': [('cmd', ['annotate']), ('filenode', None)],
841 'fa': [('cmd', ['annotate']), ('filenode', None)],
836 'mf': [('cmd', ['manifest']), ('manifest', None)],
842 'mf': [('cmd', ['manifest']), ('manifest', None)],
837 'ca': [('cmd', ['archive']), ('node', None)],
843 'ca': [('cmd', ['archive']), ('node', None)],
838 'tags': [('cmd', ['tags'])],
844 'tags': [('cmd', ['tags'])],
839 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
845 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
840 }
846 }
841
847
842 for k in shortcuts.iterkeys():
848 for k in shortcuts.iterkeys():
843 if form.has_key(k):
849 if form.has_key(k):
844 for name, value in shortcuts[k]:
850 for name, value in shortcuts[k]:
845 if value is None:
851 if value is None:
846 value = form[k]
852 value = form[k]
847 form[name] = value
853 form[name] = value
848 del form[k]
854 del form[k]
849
855
850 self.refresh()
856 self.refresh()
851
857
852 expand_form(req.form)
858 expand_form(req.form)
853
859
854 t = self.repo.ui.config("web", "templates", templatepath())
860 t = self.repo.ui.config("web", "templates", templatepath())
855 m = os.path.join(t, "map")
861 m = os.path.join(t, "map")
856 style = self.repo.ui.config("web", "style", "")
862 style = self.repo.ui.config("web", "style", "")
857 if req.form.has_key('style'):
863 if req.form.has_key('style'):
858 style = req.form['style'][0]
864 style = req.form['style'][0]
859 if style:
865 if style:
860 b = os.path.basename("map-" + style)
866 b = os.path.basename("map-" + style)
861 p = os.path.join(t, b)
867 p = os.path.join(t, b)
862 if os.path.isfile(p):
868 if os.path.isfile(p):
863 m = p
869 m = p
864
870
865 port = req.env["SERVER_PORT"]
871 port = req.env["SERVER_PORT"]
866 port = port != "80" and (":" + port) or ""
872 port = port != "80" and (":" + port) or ""
867 uri = req.env["REQUEST_URI"]
873 uri = req.env["REQUEST_URI"]
868 if "?" in uri:
874 if "?" in uri:
869 uri = uri.split("?")[0]
875 uri = uri.split("?")[0]
870 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
876 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
871 if not self.reponame:
877 if not self.reponame:
872 self.reponame = (self.repo.ui.config("web", "name")
878 self.reponame = (self.repo.ui.config("web", "name")
873 or uri.strip('/') or self.repo.root)
879 or uri.strip('/') or self.repo.root)
874
880
875 self.t = templater(m, common_filters,
881 self.t = templater(m, common_filters,
876 {"url": url,
882 {"url": url,
877 "repo": self.reponame,
883 "repo": self.reponame,
878 "header": header,
884 "header": header,
879 "footer": footer,
885 "footer": footer,
880 })
886 })
881
887
882 if not req.form.has_key('cmd'):
888 if not req.form.has_key('cmd'):
883 req.form['cmd'] = [self.t.cache['default'],]
889 req.form['cmd'] = [self.t.cache['default'],]
884
890
885 if req.form['cmd'][0] == 'changelog':
891 if req.form['cmd'][0] == 'changelog':
886 c = self.repo.changelog.count() - 1
892 c = self.repo.changelog.count() - 1
887 hi = c
893 hi = c
888 if req.form.has_key('rev'):
894 if req.form.has_key('rev'):
889 hi = req.form['rev'][0]
895 hi = req.form['rev'][0]
890 try:
896 try:
891 hi = self.repo.changelog.rev(self.repo.lookup(hi))
897 hi = self.repo.changelog.rev(self.repo.lookup(hi))
892 except hg.RepoError:
898 except hg.RepoError:
893 req.write(self.search(hi))
899 req.write(self.search(hi))
894 return
900 return
895
901
896 req.write(self.changelog(hi))
902 req.write(self.changelog(hi))
897
903
898 elif req.form['cmd'][0] == 'changeset':
904 elif req.form['cmd'][0] == 'changeset':
899 req.write(self.changeset(req.form['node'][0]))
905 req.write(self.changeset(req.form['node'][0]))
900
906
901 elif req.form['cmd'][0] == 'manifest':
907 elif req.form['cmd'][0] == 'manifest':
902 req.write(self.manifest(req.form['manifest'][0],
908 req.write(self.manifest(req.form['manifest'][0],
903 clean(req.form['path'][0])))
909 clean(req.form['path'][0])))
904
910
905 elif req.form['cmd'][0] == 'tags':
911 elif req.form['cmd'][0] == 'tags':
906 req.write(self.tags())
912 req.write(self.tags())
907
913
908 elif req.form['cmd'][0] == 'summary':
914 elif req.form['cmd'][0] == 'summary':
909 req.write(self.summary())
915 req.write(self.summary())
910
916
911 elif req.form['cmd'][0] == 'filediff':
917 elif req.form['cmd'][0] == 'filediff':
912 req.write(self.filediff(clean(req.form['file'][0]),
918 req.write(self.filediff(clean(req.form['file'][0]),
913 req.form['node'][0]))
919 req.form['node'][0]))
914
920
915 elif req.form['cmd'][0] == 'file':
921 elif req.form['cmd'][0] == 'file':
916 req.write(self.filerevision(clean(req.form['file'][0]),
922 req.write(self.filerevision(clean(req.form['file'][0]),
917 req.form['filenode'][0]))
923 req.form['filenode'][0]))
918
924
919 elif req.form['cmd'][0] == 'annotate':
925 elif req.form['cmd'][0] == 'annotate':
920 req.write(self.fileannotate(clean(req.form['file'][0]),
926 req.write(self.fileannotate(clean(req.form['file'][0]),
921 req.form['filenode'][0]))
927 req.form['filenode'][0]))
922
928
923 elif req.form['cmd'][0] == 'filelog':
929 elif req.form['cmd'][0] == 'filelog':
924 req.write(self.filelog(clean(req.form['file'][0]),
930 req.write(self.filelog(clean(req.form['file'][0]),
925 req.form['filenode'][0]))
931 req.form['filenode'][0]))
926
932
927 elif req.form['cmd'][0] == 'heads':
933 elif req.form['cmd'][0] == 'heads':
928 req.httphdr("application/mercurial-0.1")
934 req.httphdr("application/mercurial-0.1")
929 h = self.repo.heads()
935 h = self.repo.heads()
930 req.write(" ".join(map(hex, h)) + "\n")
936 req.write(" ".join(map(hex, h)) + "\n")
931
937
932 elif req.form['cmd'][0] == 'branches':
938 elif req.form['cmd'][0] == 'branches':
933 req.httphdr("application/mercurial-0.1")
939 req.httphdr("application/mercurial-0.1")
934 nodes = []
940 nodes = []
935 if req.form.has_key('nodes'):
941 if req.form.has_key('nodes'):
936 nodes = map(bin, req.form['nodes'][0].split(" "))
942 nodes = map(bin, req.form['nodes'][0].split(" "))
937 for b in self.repo.branches(nodes):
943 for b in self.repo.branches(nodes):
938 req.write(" ".join(map(hex, b)) + "\n")
944 req.write(" ".join(map(hex, b)) + "\n")
939
945
940 elif req.form['cmd'][0] == 'between':
946 elif req.form['cmd'][0] == 'between':
941 req.httphdr("application/mercurial-0.1")
947 req.httphdr("application/mercurial-0.1")
942 nodes = []
948 nodes = []
943 if req.form.has_key('pairs'):
949 if req.form.has_key('pairs'):
944 pairs = [map(bin, p.split("-"))
950 pairs = [map(bin, p.split("-"))
945 for p in req.form['pairs'][0].split(" ")]
951 for p in req.form['pairs'][0].split(" ")]
946 for b in self.repo.between(pairs):
952 for b in self.repo.between(pairs):
947 req.write(" ".join(map(hex, b)) + "\n")
953 req.write(" ".join(map(hex, b)) + "\n")
948
954
949 elif req.form['cmd'][0] == 'changegroup':
955 elif req.form['cmd'][0] == 'changegroup':
950 req.httphdr("application/mercurial-0.1")
956 req.httphdr("application/mercurial-0.1")
951 nodes = []
957 nodes = []
952 if not self.allowpull:
958 if not self.allowpull:
953 return
959 return
954
960
955 if req.form.has_key('roots'):
961 if req.form.has_key('roots'):
956 nodes = map(bin, req.form['roots'][0].split(" "))
962 nodes = map(bin, req.form['roots'][0].split(" "))
957
963
958 z = zlib.compressobj()
964 z = zlib.compressobj()
959 f = self.repo.changegroup(nodes)
965 f = self.repo.changegroup(nodes)
960 while 1:
966 while 1:
961 chunk = f.read(4096)
967 chunk = f.read(4096)
962 if not chunk:
968 if not chunk:
963 break
969 break
964 req.write(z.compress(chunk))
970 req.write(z.compress(chunk))
965
971
966 req.write(z.flush())
972 req.write(z.flush())
967
973
968 elif req.form['cmd'][0] == 'archive':
974 elif req.form['cmd'][0] == 'archive':
969 changeset = self.repo.lookup(req.form['node'][0])
975 changeset = self.repo.lookup(req.form['node'][0])
970 type = req.form['type'][0]
976 type = req.form['type'][0]
971 if (type in self.archives and
977 if (type in self.archives and
972 self.repo.ui.configbool("web", "allow" + type, False)):
978 self.repo.ui.configbool("web", "allow" + type, False)):
973 self.archive(req, changeset, type)
979 self.archive(req, changeset, type)
974 return
980 return
975
981
976 req.write(self.t("error"))
982 req.write(self.t("error"))
977
983
978 else:
984 else:
979 req.write(self.t("error"))
985 req.write(self.t("error"))
980
986
981 def create_server(repo):
987 def create_server(repo):
982
988
983 def openlog(opt, default):
989 def openlog(opt, default):
984 if opt and opt != '-':
990 if opt and opt != '-':
985 return open(opt, 'w')
991 return open(opt, 'w')
986 return default
992 return default
987
993
988 address = repo.ui.config("web", "address", "")
994 address = repo.ui.config("web", "address", "")
989 port = int(repo.ui.config("web", "port", 8000))
995 port = int(repo.ui.config("web", "port", 8000))
990 use_ipv6 = repo.ui.configbool("web", "ipv6")
996 use_ipv6 = repo.ui.configbool("web", "ipv6")
991 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
997 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
992 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
998 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
993
999
994 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
1000 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
995 address_family = getattr(socket, 'AF_INET6', None)
1001 address_family = getattr(socket, 'AF_INET6', None)
996
1002
997 def __init__(self, *args, **kwargs):
1003 def __init__(self, *args, **kwargs):
998 if self.address_family is None:
1004 if self.address_family is None:
999 raise hg.RepoError(_('IPv6 not available on this system'))
1005 raise hg.RepoError(_('IPv6 not available on this system'))
1000 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
1006 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
1001
1007
1002 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
1008 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
1003 def log_error(self, format, *args):
1009 def log_error(self, format, *args):
1004 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
1010 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
1005 self.log_date_time_string(),
1011 self.log_date_time_string(),
1006 format % args))
1012 format % args))
1007
1013
1008 def log_message(self, format, *args):
1014 def log_message(self, format, *args):
1009 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
1015 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
1010 self.log_date_time_string(),
1016 self.log_date_time_string(),
1011 format % args))
1017 format % args))
1012
1018
1013 def do_POST(self):
1019 def do_POST(self):
1014 try:
1020 try:
1015 self.do_hgweb()
1021 self.do_hgweb()
1016 except socket.error, inst:
1022 except socket.error, inst:
1017 if inst[0] != errno.EPIPE:
1023 if inst[0] != errno.EPIPE:
1018 raise
1024 raise
1019
1025
1020 def do_GET(self):
1026 def do_GET(self):
1021 self.do_POST()
1027 self.do_POST()
1022
1028
1023 def do_hgweb(self):
1029 def do_hgweb(self):
1024 query = ""
1030 query = ""
1025 p = self.path.find("?")
1031 p = self.path.find("?")
1026 if p:
1032 if p:
1027 query = self.path[p + 1:]
1033 query = self.path[p + 1:]
1028 query = query.replace('+', ' ')
1034 query = query.replace('+', ' ')
1029
1035
1030 env = {}
1036 env = {}
1031 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1037 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1032 env['REQUEST_METHOD'] = self.command
1038 env['REQUEST_METHOD'] = self.command
1033 env['SERVER_NAME'] = self.server.server_name
1039 env['SERVER_NAME'] = self.server.server_name
1034 env['SERVER_PORT'] = str(self.server.server_port)
1040 env['SERVER_PORT'] = str(self.server.server_port)
1035 env['REQUEST_URI'] = "/"
1041 env['REQUEST_URI'] = "/"
1036 if query:
1042 if query:
1037 env['QUERY_STRING'] = query
1043 env['QUERY_STRING'] = query
1038 host = self.address_string()
1044 host = self.address_string()
1039 if host != self.client_address[0]:
1045 if host != self.client_address[0]:
1040 env['REMOTE_HOST'] = host
1046 env['REMOTE_HOST'] = host
1041 env['REMOTE_ADDR'] = self.client_address[0]
1047 env['REMOTE_ADDR'] = self.client_address[0]
1042
1048
1043 if self.headers.typeheader is None:
1049 if self.headers.typeheader is None:
1044 env['CONTENT_TYPE'] = self.headers.type
1050 env['CONTENT_TYPE'] = self.headers.type
1045 else:
1051 else:
1046 env['CONTENT_TYPE'] = self.headers.typeheader
1052 env['CONTENT_TYPE'] = self.headers.typeheader
1047 length = self.headers.getheader('content-length')
1053 length = self.headers.getheader('content-length')
1048 if length:
1054 if length:
1049 env['CONTENT_LENGTH'] = length
1055 env['CONTENT_LENGTH'] = length
1050 accept = []
1056 accept = []
1051 for line in self.headers.getallmatchingheaders('accept'):
1057 for line in self.headers.getallmatchingheaders('accept'):
1052 if line[:1] in "\t\n\r ":
1058 if line[:1] in "\t\n\r ":
1053 accept.append(line.strip())
1059 accept.append(line.strip())
1054 else:
1060 else:
1055 accept = accept + line[7:].split(',')
1061 accept = accept + line[7:].split(',')
1056 env['HTTP_ACCEPT'] = ','.join(accept)
1062 env['HTTP_ACCEPT'] = ','.join(accept)
1057
1063
1058 req = hgrequest(self.rfile, self.wfile, env)
1064 req = hgrequest(self.rfile, self.wfile, env)
1059 self.send_response(200, "Script output follows")
1065 self.send_response(200, "Script output follows")
1060 hg.run(req)
1066 hg.run(req)
1061
1067
1062 hg = hgweb(repo)
1068 hg = hgweb(repo)
1063 if use_ipv6:
1069 if use_ipv6:
1064 return IPv6HTTPServer((address, port), hgwebhandler)
1070 return IPv6HTTPServer((address, port), hgwebhandler)
1065 else:
1071 else:
1066 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
1072 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
1067
1073
1068 # This is a stopgap
1074 # This is a stopgap
1069 class hgwebdir(object):
1075 class hgwebdir(object):
1070 def __init__(self, config):
1076 def __init__(self, config):
1071 def cleannames(items):
1077 def cleannames(items):
1072 return [(name.strip('/'), path) for name, path in items]
1078 return [(name.strip('/'), path) for name, path in items]
1073
1079
1074 if type(config) == type([]):
1080 if type(config) == type([]):
1075 self.repos = cleannames(config)
1081 self.repos = cleannames(config)
1076 elif type(config) == type({}):
1082 elif type(config) == type({}):
1077 self.repos = cleannames(config.items())
1083 self.repos = cleannames(config.items())
1078 self.repos.sort()
1084 self.repos.sort()
1079 else:
1085 else:
1080 cp = ConfigParser.SafeConfigParser()
1086 cp = ConfigParser.SafeConfigParser()
1081 cp.read(config)
1087 cp.read(config)
1082 self.repos = cleannames(cp.items("paths"))
1088 self.repos = cleannames(cp.items("paths"))
1083 self.repos.sort()
1089 self.repos.sort()
1084
1090
1085 def run(self, req=hgrequest()):
1091 def run(self, req=hgrequest()):
1086 def header(**map):
1092 def header(**map):
1087 yield tmpl("header", **map)
1093 yield tmpl("header", **map)
1088
1094
1089 def footer(**map):
1095 def footer(**map):
1090 yield tmpl("footer", **map)
1096 yield tmpl("footer", **map)
1091
1097
1092 m = os.path.join(templatepath(), "map")
1098 m = os.path.join(templatepath(), "map")
1093 tmpl = templater(m, common_filters,
1099 tmpl = templater(m, common_filters,
1094 {"header": header, "footer": footer})
1100 {"header": header, "footer": footer})
1095
1101
1096 def entries(**map):
1102 def entries(**map):
1097 parity = 0
1103 parity = 0
1098 for name, path in self.repos:
1104 for name, path in self.repos:
1099 u = ui.ui()
1105 u = ui.ui()
1100 try:
1106 try:
1101 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
1107 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
1102 except IOError:
1108 except IOError:
1103 pass
1109 pass
1104 get = u.config
1110 get = u.config
1105
1111
1106 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
1112 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
1107 .replace("//", "/"))
1113 .replace("//", "/"))
1108
1114
1109 # update time with local timezone
1115 # update time with local timezone
1110 try:
1116 try:
1111 d = (get_mtime(path), util.makedate()[1])
1117 d = (get_mtime(path), util.makedate()[1])
1112 except OSError:
1118 except OSError:
1113 continue
1119 continue
1114
1120
1115 yield dict(contact=(get("ui", "username") or # preferred
1121 yield dict(contact=(get("ui", "username") or # preferred
1116 get("web", "contact") or # deprecated
1122 get("web", "contact") or # deprecated
1117 get("web", "author", "unknown")), # also
1123 get("web", "author", "unknown")), # also
1118 name=get("web", "name", name),
1124 name=get("web", "name", name),
1119 url=url,
1125 url=url,
1120 parity=parity,
1126 parity=parity,
1121 shortdesc=get("web", "description", "unknown"),
1127 shortdesc=get("web", "description", "unknown"),
1122 lastupdate=d)
1128 lastupdate=d)
1123
1129
1124 parity = 1 - parity
1130 parity = 1 - parity
1125
1131
1126 virtual = req.env.get("PATH_INFO", "").strip('/')
1132 virtual = req.env.get("PATH_INFO", "").strip('/')
1127 if virtual:
1133 if virtual:
1128 real = dict(self.repos).get(virtual)
1134 real = dict(self.repos).get(virtual)
1129 if real:
1135 if real:
1130 try:
1136 try:
1131 hgweb(real).run(req)
1137 hgweb(real).run(req)
1132 except IOError, inst:
1138 except IOError, inst:
1133 req.write(tmpl("error", error=inst.strerror))
1139 req.write(tmpl("error", error=inst.strerror))
1134 except hg.RepoError, inst:
1140 except hg.RepoError, inst:
1135 req.write(tmpl("error", error=str(inst)))
1141 req.write(tmpl("error", error=str(inst)))
1136 else:
1142 else:
1137 req.write(tmpl("notfound", repo=virtual))
1143 req.write(tmpl("notfound", repo=virtual))
1138 else:
1144 else:
1139 req.write(tmpl("index", entries=entries))
1145 req.write(tmpl("index", entries=entries))
General Comments 0
You need to be logged in to leave comments. Login now