##// END OF EJS Templates
hgweb: fix merge breakage
Matt Mackall -
r6437:10152603 default
parent child Browse files
Show More
@@ -1,576 +1,576
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, mimetypes, re
8 import os, mimetypes, re
9 import webutil
9 import webutil
10 from mercurial import revlog, archival
10 from mercurial import revlog, archival
11 from mercurial.node import short, hex, nullid
11 from mercurial.node import short, hex, nullid
12 from mercurial.util import binary
12 from mercurial.util import binary
13 from mercurial.repo import RepoError
13 from mercurial.repo import RepoError
14 from common import paritygen, staticfile, get_contact, ErrorResponse
14 from common import paritygen, staticfile, get_contact, ErrorResponse
15 from common import HTTP_OK, HTTP_NOT_FOUND
15 from common import HTTP_OK, HTTP_NOT_FOUND
16
16
17 # __all__ is populated with the allowed commands. Be sure to add to it if
17 # __all__ is populated with the allowed commands. Be sure to add to it if
18 # you're adding a new command, or the new command won't work.
18 # you're adding a new command, or the new command won't work.
19
19
20 __all__ = [
20 __all__ = [
21 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
21 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
22 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog',
22 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog',
23 'archive', 'static',
23 'archive', 'static',
24 ]
24 ]
25
25
26 def log(web, req, tmpl):
26 def log(web, req, tmpl):
27 if 'file' in req.form and req.form['file'][0]:
27 if 'file' in req.form and req.form['file'][0]:
28 return filelog(web, req, tmpl)
28 return filelog(web, req, tmpl)
29 else:
29 else:
30 return changelog(web, req, tmpl)
30 return changelog(web, req, tmpl)
31
31
32 def rawfile(web, req, tmpl):
32 def rawfile(web, req, tmpl):
33 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
33 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
34 if not path:
34 if not path:
35 content = manifest(web, req, tmpl)
35 content = manifest(web, req, tmpl)
36 req.respond(HTTP_OK, web.ctype)
36 req.respond(HTTP_OK, web.ctype)
37 return content
37 return content
38
38
39 try:
39 try:
40 fctx = webutil.filectx(web.repo, req)
40 fctx = webutil.filectx(web.repo, req)
41 except revlog.LookupError, inst:
41 except revlog.LookupError, inst:
42 try:
42 try:
43 content = manifest(web, req, tmpl)
43 content = manifest(web, req, tmpl)
44 req.respond(HTTP_OK, web.ctype)
44 req.respond(HTTP_OK, web.ctype)
45 return content
45 return content
46 except ErrorResponse:
46 except ErrorResponse:
47 raise inst
47 raise inst
48
48
49 path = fctx.path()
49 path = fctx.path()
50 text = fctx.data()
50 text = fctx.data()
51 mt = mimetypes.guess_type(path)[0]
51 mt = mimetypes.guess_type(path)[0]
52 if mt is None or binary(text):
52 if mt is None or binary(text):
53 mt = mt or 'application/octet-stream'
53 mt = mt or 'application/octet-stream'
54
54
55 req.respond(HTTP_OK, mt, path, len(text))
55 req.respond(HTTP_OK, mt, path, len(text))
56 return [text]
56 return [text]
57
57
58 def _filerevision(web, tmpl, fctx):
58 def _filerevision(web, tmpl, fctx):
59 f = fctx.path()
59 f = fctx.path()
60 text = fctx.data()
60 text = fctx.data()
61 fl = fctx.filelog()
61 fl = fctx.filelog()
62 n = fctx.filenode()
62 n = fctx.filenode()
63 parity = paritygen(web.stripecount)
63 parity = paritygen(web.stripecount)
64
64
65 if binary(text):
65 if binary(text):
66 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
66 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
67 text = '(binary:%s)' % mt
67 text = '(binary:%s)' % mt
68
68
69 def lines():
69 def lines():
70 for lineno, t in enumerate(text.splitlines(1)):
70 for lineno, t in enumerate(text.splitlines(1)):
71 yield {"line": t,
71 yield {"line": t,
72 "lineid": "l%d" % (lineno + 1),
72 "lineid": "l%d" % (lineno + 1),
73 "linenumber": "% 6d" % (lineno + 1),
73 "linenumber": "% 6d" % (lineno + 1),
74 "parity": parity.next()}
74 "parity": parity.next()}
75
75
76 return tmpl("filerevision",
76 return tmpl("filerevision",
77 file=f,
77 file=f,
78 path=webutil.up(f),
78 path=webutil.up(f),
79 text=lines(),
79 text=lines(),
80 rev=fctx.rev(),
80 rev=fctx.rev(),
81 node=hex(fctx.node()),
81 node=hex(fctx.node()),
82 author=fctx.user(),
82 author=fctx.user(),
83 date=fctx.date(),
83 date=fctx.date(),
84 desc=fctx.description(),
84 desc=fctx.description(),
85 branch=webutil.nodebranchnodefault(fctx),
85 branch=webutil.nodebranchnodefault(fctx),
86 parent=webutil.siblings(fctx.parents()),
86 parent=webutil.siblings(fctx.parents()),
87 child=webutil.siblings(fctx.children()),
87 child=webutil.siblings(fctx.children()),
88 rename=webutil.renamelink(fctx),
88 rename=webutil.renamelink(fctx),
89 permissions=fctx.manifest().flags(f))
89 permissions=fctx.manifest().flags(f))
90
90
91 def file(web, req, tmpl):
91 def file(web, req, tmpl):
92 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
92 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
93 if path:
93 if path:
94 try:
94 try:
95 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
95 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
96 except revlog.LookupError, inst:
96 except revlog.LookupError, inst:
97 pass
97 pass
98
98
99 try:
99 try:
100 return manifest(web, req, tmpl)
100 return manifest(web, req, tmpl)
101 except ErrorResponse:
101 except ErrorResponse:
102 raise inst
102 raise inst
103
103
104 def _search(web, tmpl, query):
104 def _search(web, tmpl, query):
105
105
106 def changelist(**map):
106 def changelist(**map):
107 cl = web.repo.changelog
107 cl = web.repo.changelog
108 count = 0
108 count = 0
109 qw = query.lower().split()
109 qw = query.lower().split()
110
110
111 def revgen():
111 def revgen():
112 for i in xrange(cl.count() - 1, 0, -100):
112 for i in xrange(cl.count() - 1, 0, -100):
113 l = []
113 l = []
114 for j in xrange(max(0, i - 100), i + 1):
114 for j in xrange(max(0, i - 100), i + 1):
115 ctx = web.repo.changectx(j)
115 ctx = web.repo.changectx(j)
116 l.append(ctx)
116 l.append(ctx)
117 l.reverse()
117 l.reverse()
118 for e in l:
118 for e in l:
119 yield e
119 yield e
120
120
121 for ctx in revgen():
121 for ctx in revgen():
122 miss = 0
122 miss = 0
123 for q in qw:
123 for q in qw:
124 if not (q in ctx.user().lower() or
124 if not (q in ctx.user().lower() or
125 q in ctx.description().lower() or
125 q in ctx.description().lower() or
126 q in " ".join(ctx.files()).lower()):
126 q in " ".join(ctx.files()).lower()):
127 miss = 1
127 miss = 1
128 break
128 break
129 if miss:
129 if miss:
130 continue
130 continue
131
131
132 count = 1
132 count = 1
133 n = ctx.node()
133 n = ctx.node()
134 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
134 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
135
135
136 yield tmpl('searchentry',
136 yield tmpl('searchentry',
137 parity=parity.next(),
137 parity=parity.next(),
138 author=ctx.user(),
138 author=ctx.user(),
139 parent=webutil.siblings(ctx.parents()),
139 parent=webutil.siblings(ctx.parents()),
140 child=webutil.siblings(ctx.children()),
140 child=webutil.siblings(ctx.children()),
141 changelogtag=showtags,
141 changelogtag=showtags,
142 desc=ctx.description(),
142 desc=ctx.description(),
143 date=ctx.date(),
143 date=ctx.date(),
144 files=web.listfilediffs(tmpl, ctx.files(), n),
144 files=web.listfilediffs(tmpl, ctx.files(), n),
145 rev=ctx.rev(),
145 rev=ctx.rev(),
146 node=hex(n),
146 node=hex(n),
147 tags=webutil.nodetagsdict(web.repo, n),
147 tags=webutil.nodetagsdict(web.repo, n),
148 inbranch=webutil.nodeinbranch(web.repo, ctx),
148 inbranch=webutil.nodeinbranch(web.repo, ctx),
149 branches=webutil.nodebranchdict(web.repo, ctx))
149 branches=webutil.nodebranchdict(web.repo, ctx))
150
150
151 if count >= web.maxchanges:
151 if count >= web.maxchanges:
152 break
152 break
153
153
154 cl = web.repo.changelog
154 cl = web.repo.changelog
155 parity = paritygen(web.stripecount)
155 parity = paritygen(web.stripecount)
156
156
157 return tmpl('search',
157 return tmpl('search',
158 query=query,
158 query=query,
159 node=hex(cl.tip()),
159 node=hex(cl.tip()),
160 entries=changelist,
160 entries=changelist,
161 archives=web.archivelist("tip"))
161 archives=web.archivelist("tip"))
162
162
163 def changelog(web, req, tmpl, shortlog = False):
163 def changelog(web, req, tmpl, shortlog = False):
164 if 'node' in req.form:
164 if 'node' in req.form:
165 ctx = webutil.changectx(web.repo, req)
165 ctx = webutil.changectx(web.repo, req)
166 else:
166 else:
167 if 'rev' in req.form:
167 if 'rev' in req.form:
168 hi = req.form['rev'][0]
168 hi = req.form['rev'][0]
169 else:
169 else:
170 hi = web.repo.changelog.count() - 1
170 hi = web.repo.changelog.count() - 1
171 try:
171 try:
172 ctx = web.repo.changectx(hi)
172 ctx = web.repo.changectx(hi)
173 except RepoError:
173 except RepoError:
174 return _search(web, tmpl, hi) # XXX redirect to 404 page?
174 return _search(web, tmpl, hi) # XXX redirect to 404 page?
175
175
176 def changelist(limit=0, **map):
176 def changelist(limit=0, **map):
177 cl = web.repo.changelog
177 cl = web.repo.changelog
178 l = [] # build a list in forward order for efficiency
178 l = [] # build a list in forward order for efficiency
179 for i in xrange(start, end):
179 for i in xrange(start, end):
180 ctx = web.repo.changectx(i)
180 ctx = web.repo.changectx(i)
181 n = ctx.node()
181 n = ctx.node()
182 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
182 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
183
183
184 l.insert(0, {"parity": parity.next(),
184 l.insert(0, {"parity": parity.next(),
185 "author": ctx.user(),
185 "author": ctx.user(),
186 "parent": webutil.siblings(ctx.parents(), i - 1),
186 "parent": webutil.siblings(ctx.parents(), i - 1),
187 "child": webutil.siblings(ctx.children(), i + 1),
187 "child": webutil.siblings(ctx.children(), i + 1),
188 "changelogtag": showtags,
188 "changelogtag": showtags,
189 "desc": ctx.description(),
189 "desc": ctx.description(),
190 "date": ctx.date(),
190 "date": ctx.date(),
191 "files": web.listfilediffs(tmpl, ctx.files(), n),
191 "files": web.listfilediffs(tmpl, ctx.files(), n),
192 "rev": i,
192 "rev": i,
193 "node": hex(n),
193 "node": hex(n),
194 "tags": webutil.nodetagsdict(web.repo, n),
194 "tags": webutil.nodetagsdict(web.repo, n),
195 "inbranch": webutil.nodeinbranch(web.repo, ctx),
195 "inbranch": webutil.nodeinbranch(web.repo, ctx),
196 "branches": webutil.nodebranchdict(web.repo, ctx)
196 "branches": webutil.nodebranchdict(web.repo, ctx)
197 })
197 })
198
198
199 if limit > 0:
199 if limit > 0:
200 l = l[:limit]
200 l = l[:limit]
201
201
202 for e in l:
202 for e in l:
203 yield e
203 yield e
204
204
205 maxchanges = shortlog and web.maxshortchanges or web.maxchanges
205 maxchanges = shortlog and web.maxshortchanges or web.maxchanges
206 cl = web.repo.changelog
206 cl = web.repo.changelog
207 count = cl.count()
207 count = cl.count()
208 pos = ctx.rev()
208 pos = ctx.rev()
209 start = max(0, pos - maxchanges + 1)
209 start = max(0, pos - maxchanges + 1)
210 end = min(count, start + maxchanges)
210 end = min(count, start + maxchanges)
211 pos = end - 1
211 pos = end - 1
212 parity = paritygen(web.stripecount, offset=start-end)
212 parity = paritygen(web.stripecount, offset=start-end)
213
213
214 changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
214 changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx)
215
215
216 return tmpl(shortlog and 'shortlog' or 'changelog',
216 return tmpl(shortlog and 'shortlog' or 'changelog',
217 changenav=changenav,
217 changenav=changenav,
218 node=hex(ctx.node()),
218 node=hex(ctx.node()),
219 rev=pos, changesets=count,
219 rev=pos, changesets=count,
220 entries=lambda **x: changelist(limit=0,**x),
220 entries=lambda **x: changelist(limit=0,**x),
221 latestentry=lambda **x: changelist(limit=1,**x),
221 latestentry=lambda **x: changelist(limit=1,**x),
222 archives=web.archivelist("tip"))
222 archives=web.archivelist("tip"))
223
223
224 def shortlog(web, req, tmpl):
224 def shortlog(web, req, tmpl):
225 return changelog(web, req, tmpl, shortlog = True)
225 return changelog(web, req, tmpl, shortlog = True)
226
226
227 def changeset(web, req, tmpl):
227 def changeset(web, req, tmpl):
228 ctx = webutil.changectx(web.repo, req)
228 ctx = webutil.changectx(web.repo, req)
229 n = ctx.node()
229 n = ctx.node()
230 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n)
230 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', n)
231 parents = ctx.parents()
231 parents = ctx.parents()
232 p1 = parents[0].node()
232 p1 = parents[0].node()
233
233
234 files = []
234 files = []
235 parity = paritygen(web.stripecount)
235 parity = paritygen(web.stripecount)
236 for f in ctx.files():
236 for f in ctx.files():
237 files.append(tmpl("filenodelink",
237 files.append(tmpl("filenodelink",
238 node=hex(n), file=f,
238 node=hex(n), file=f,
239 parity=parity.next()))
239 parity=parity.next()))
240
240
241 diffs = web.diff(tmpl, p1, n, None)
241 diffs = web.diff(tmpl, p1, n, None)
242 return tmpl('changeset',
242 return tmpl('changeset',
243 diff=diffs,
243 diff=diffs,
244 rev=ctx.rev(),
244 rev=ctx.rev(),
245 node=hex(n),
245 node=hex(n),
246 parent=webutil.siblings(parents),
246 parent=webutil.siblings(parents),
247 child=webutil.siblings(ctx.children()),
247 child=webutil.siblings(ctx.children()),
248 changesettag=showtags,
248 changesettag=showtags,
249 author=ctx.user(),
249 author=ctx.user(),
250 desc=ctx.description(),
250 desc=ctx.description(),
251 date=ctx.date(),
251 date=ctx.date(),
252 files=files,
252 files=files,
253 archives=web.archivelist(hex(n)),
253 archives=web.archivelist(hex(n)),
254 tags=webutil.nodetagsdict(web.repo, n),
254 tags=webutil.nodetagsdict(web.repo, n),
255 branch=webutil.nodebranchnodefault(ctx),
255 branch=webutil.nodebranchnodefault(ctx),
256 inbranch=webutil.nodeinbranch(web.repo, ctx),
256 inbranch=webutil.nodeinbranch(web.repo, ctx),
257 branches=webutil.nodebranchdict(web.repo, ctx))
257 branches=webutil.nodebranchdict(web.repo, ctx))
258
258
259 rev = changeset
259 rev = changeset
260
260
261 def manifest(web, req, tmpl):
261 def manifest(web, req, tmpl):
262 ctx = webutil.changectx(web.repo, req)
262 ctx = webutil.changectx(web.repo, req)
263 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
263 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
264 mf = ctx.manifest()
264 mf = ctx.manifest()
265 node = ctx.node()
265 node = ctx.node()
266
266
267 files = {}
267 files = {}
268 parity = paritygen(web.stripecount)
268 parity = paritygen(web.stripecount)
269
269
270 if path and path[-1] != "/":
270 if path and path[-1] != "/":
271 path += "/"
271 path += "/"
272 l = len(path)
272 l = len(path)
273 abspath = "/" + path
273 abspath = "/" + path
274
274
275 for f, n in mf.items():
275 for f, n in mf.items():
276 if f[:l] != path:
276 if f[:l] != path:
277 continue
277 continue
278 remain = f[l:]
278 remain = f[l:]
279 if "/" in remain:
279 if "/" in remain:
280 short = remain[:remain.index("/") + 1] # bleah
280 short = remain[:remain.index("/") + 1] # bleah
281 files[short] = (f, None)
281 files[short] = (f, None)
282 else:
282 else:
283 short = os.path.basename(remain)
283 short = os.path.basename(remain)
284 files[short] = (f, n)
284 files[short] = (f, n)
285
285
286 if not files:
286 if not files:
287 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
287 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
288
288
289 def filelist(**map):
289 def filelist(**map):
290 fl = files.keys()
290 fl = files.keys()
291 fl.sort()
291 fl.sort()
292 for f in fl:
292 for f in fl:
293 full, fnode = files[f]
293 full, fnode = files[f]
294 if not fnode:
294 if not fnode:
295 continue
295 continue
296
296
297 fctx = ctx.filectx(full)
297 fctx = ctx.filectx(full)
298 yield {"file": full,
298 yield {"file": full,
299 "parity": parity.next(),
299 "parity": parity.next(),
300 "basename": f,
300 "basename": f,
301 "date": fctx.changectx().date(),
301 "date": fctx.changectx().date(),
302 "size": fctx.size(),
302 "size": fctx.size(),
303 "permissions": mf.flags(full)}
303 "permissions": mf.flags(full)}
304
304
305 def dirlist(**map):
305 def dirlist(**map):
306 fl = files.keys()
306 fl = files.keys()
307 fl.sort()
307 fl.sort()
308 for f in fl:
308 for f in fl:
309 full, fnode = files[f]
309 full, fnode = files[f]
310 if fnode:
310 if fnode:
311 continue
311 continue
312
312
313 yield {"parity": parity.next(),
313 yield {"parity": parity.next(),
314 "path": "%s%s" % (abspath, f),
314 "path": "%s%s" % (abspath, f),
315 "basename": f[:-1]}
315 "basename": f[:-1]}
316
316
317 return tmpl("manifest",
317 return tmpl("manifest",
318 rev=ctx.rev(),
318 rev=ctx.rev(),
319 node=hex(node),
319 node=hex(node),
320 path=abspath,
320 path=abspath,
321 up=webutil.up(abspath),
321 up=webutil.up(abspath),
322 upparity=parity.next(),
322 upparity=parity.next(),
323 fentries=filelist,
323 fentries=filelist,
324 dentries=dirlist,
324 dentries=dirlist,
325 archives=web.archivelist(hex(node)),
325 archives=web.archivelist(hex(node)),
326 tags=webutil.nodetagsdict(web.repo, node),
326 tags=webutil.nodetagsdict(web.repo, node),
327 inbranch=webutil.nodeinbranch(web.repo, ctx),
327 inbranch=webutil.nodeinbranch(web.repo, ctx),
328 branches=webutil.nodebranchdict(web.repo, ctx))
328 branches=webutil.nodebranchdict(web.repo, ctx))
329
329
330 def tags(web, req, tmpl):
330 def tags(web, req, tmpl):
331 i = web.repo.tagslist()
331 i = web.repo.tagslist()
332 i.reverse()
332 i.reverse()
333 parity = paritygen(web.stripecount)
333 parity = paritygen(web.stripecount)
334
334
335 def entries(notip=False,limit=0, **map):
335 def entries(notip=False,limit=0, **map):
336 count = 0
336 count = 0
337 for k, n in i:
337 for k, n in i:
338 if notip and k == "tip":
338 if notip and k == "tip":
339 continue
339 continue
340 if limit > 0 and count >= limit:
340 if limit > 0 and count >= limit:
341 continue
341 continue
342 count = count + 1
342 count = count + 1
343 yield {"parity": parity.next(),
343 yield {"parity": parity.next(),
344 "tag": k,
344 "tag": k,
345 "date": web.repo.changectx(n).date(),
345 "date": web.repo.changectx(n).date(),
346 "node": hex(n)}
346 "node": hex(n)}
347
347
348 return tmpl("tags",
348 return tmpl("tags",
349 node=hex(web.repo.changelog.tip()),
349 node=hex(web.repo.changelog.tip()),
350 entries=lambda **x: entries(False,0, **x),
350 entries=lambda **x: entries(False,0, **x),
351 entriesnotip=lambda **x: entries(True,0, **x),
351 entriesnotip=lambda **x: entries(True,0, **x),
352 latestentry=lambda **x: entries(True,1, **x))
352 latestentry=lambda **x: entries(True,1, **x))
353
353
354 def summary(web, req, tmpl):
354 def summary(web, req, tmpl):
355 i = web.repo.tagslist()
355 i = web.repo.tagslist()
356 i.reverse()
356 i.reverse()
357
357
358 def tagentries(**map):
358 def tagentries(**map):
359 parity = paritygen(web.stripecount)
359 parity = paritygen(web.stripecount)
360 count = 0
360 count = 0
361 for k, n in i:
361 for k, n in i:
362 if k == "tip": # skip tip
362 if k == "tip": # skip tip
363 continue
363 continue
364
364
365 count = 1
365 count = 1
366 if count > 10: # limit to 10 tags
366 if count > 10: # limit to 10 tags
367 break
367 break
368
368
369 yield tmpl("tagentry",
369 yield tmpl("tagentry",
370 parity=parity.next(),
370 parity=parity.next(),
371 tag=k,
371 tag=k,
372 node=hex(n),
372 node=hex(n),
373 date=web.repo.changectx(n).date())
373 date=web.repo.changectx(n).date())
374
374
375 def branches(**map):
375 def branches(**map):
376 parity = paritygen(web.stripecount)
376 parity = paritygen(web.stripecount)
377
377
378 b = web.repo.branchtags()
378 b = web.repo.branchtags()
379 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
379 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
380 l.sort()
380 l.sort()
381
381
382 for r,n,t in l:
382 for r,n,t in l:
383 ctx = web.repo.changectx(n)
383 ctx = web.repo.changectx(n)
384 yield {'parity': parity.next(),
384 yield {'parity': parity.next(),
385 'branch': t,
385 'branch': t,
386 'node': hex(n),
386 'node': hex(n),
387 'date': ctx.date()}
387 'date': ctx.date()}
388
388
389 def changelist(**map):
389 def changelist(**map):
390 parity = paritygen(web.stripecount, offset=start-end)
390 parity = paritygen(web.stripecount, offset=start-end)
391 l = [] # build a list in forward order for efficiency
391 l = [] # build a list in forward order for efficiency
392 for i in xrange(start, end):
392 for i in xrange(start, end):
393 ctx = web.repo.changectx(i)
393 ctx = web.repo.changectx(i)
394 n = ctx.node()
394 n = ctx.node()
395 hn = hex(n)
395 hn = hex(n)
396
396
397 l.insert(0, tmpl(
397 l.insert(0, tmpl(
398 'shortlogentry',
398 'shortlogentry',
399 parity=parity.next(),
399 parity=parity.next(),
400 author=ctx.user(),
400 author=ctx.user(),
401 desc=ctx.description(),
401 desc=ctx.description(),
402 date=ctx.date(),
402 date=ctx.date(),
403 rev=i,
403 rev=i,
404 node=hn,
404 node=hn,
405 tags=webutil.nodetagsdict(web.repo, n),
405 tags=webutil.nodetagsdict(web.repo, n),
406 inbranch=webutil.nodeinbranch(web.repo, ctx),
406 inbranch=webutil.nodeinbranch(web.repo, ctx),
407 branches=webutil.nodebranchdict(web.repo, ctx)))
407 branches=webutil.nodebranchdict(web.repo, ctx)))
408
408
409 yield l
409 yield l
410
410
411 cl = web.repo.changelog
411 cl = web.repo.changelog
412 count = cl.count()
412 count = cl.count()
413 start = max(0, count - web.maxchanges)
413 start = max(0, count - web.maxchanges)
414 end = min(count, start + web.maxchanges)
414 end = min(count, start + web.maxchanges)
415
415
416 return tmpl("summary",
416 return tmpl("summary",
417 desc=web.config("web", "description", "unknown"),
417 desc=web.config("web", "description", "unknown"),
418 owner=get_contact(web.config) or "unknown",
418 owner=get_contact(web.config) or "unknown",
419 lastchange=cl.read(cl.tip())[2],
419 lastchange=cl.read(cl.tip())[2],
420 tags=tagentries,
420 tags=tagentries,
421 branches=branches,
421 branches=branches,
422 shortlog=changelist,
422 shortlog=changelist,
423 node=hex(cl.tip()),
423 node=hex(cl.tip()),
424 archives=web.archivelist("tip"))
424 archives=web.archivelist("tip"))
425
425
426 def filediff(web, req, tmpl):
426 def filediff(web, req, tmpl):
427 fctx = webutil.filectx(web.repo, req)
427 fctx = webutil.filectx(web.repo, req)
428 n = fctx.node()
428 n = fctx.node()
429 path = fctx.path()
429 path = fctx.path()
430 parents = fctx.parents()
430 parents = fctx.parents()
431 p1 = parents and parents[0].node() or nullid
431 p1 = parents and parents[0].node() or nullid
432
432
433 diffs = web.diff(tmpl, p1, n, [path])
433 diffs = web.diff(tmpl, p1, n, [path])
434 return tmpl("filediff",
434 return tmpl("filediff",
435 file=path,
435 file=path,
436 node=hex(n),
436 node=hex(n),
437 rev=fctx.rev(),
437 rev=fctx.rev(),
438 date=fctx.date(),
438 date=fctx.date(),
439 desc=fctx.description(),
439 desc=fctx.description(),
440 author=fctx.user(),
440 author=fctx.user(),
441 rename=self.renamelink(fctx),
441 rename=webutil.renamelink(fctx),
442 branch=webutil.nodebranchnodefault(fctx),
442 branch=webutil.nodebranchnodefault(fctx),
443 parent=webutil.siblings(parents),
443 parent=webutil.siblings(parents),
444 child=webutil.siblings(fctx.children()),
444 child=webutil.siblings(fctx.children()),
445 diff=diffs)
445 diff=diffs)
446
446
447 diff = filediff
447 diff = filediff
448
448
449 def annotate(web, req, tmpl):
449 def annotate(web, req, tmpl):
450 fctx = webutil.filectx(web.repo, req)
450 fctx = webutil.filectx(web.repo, req)
451 f = fctx.path()
451 f = fctx.path()
452 n = fctx.filenode()
452 n = fctx.filenode()
453 fl = fctx.filelog()
453 fl = fctx.filelog()
454 parity = paritygen(web.stripecount)
454 parity = paritygen(web.stripecount)
455
455
456 def annotate(**map):
456 def annotate(**map):
457 last = None
457 last = None
458 if binary(fctx.data()):
458 if binary(fctx.data()):
459 mt = (mimetypes.guess_type(fctx.path())[0]
459 mt = (mimetypes.guess_type(fctx.path())[0]
460 or 'application/octet-stream')
460 or 'application/octet-stream')
461 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
461 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
462 '(binary:%s)' % mt)])
462 '(binary:%s)' % mt)])
463 else:
463 else:
464 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
464 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
465 for lineno, ((f, targetline), l) in lines:
465 for lineno, ((f, targetline), l) in lines:
466 fnode = f.filenode()
466 fnode = f.filenode()
467 name = web.repo.ui.shortuser(f.user())
467 name = web.repo.ui.shortuser(f.user())
468
468
469 if last != fnode:
469 if last != fnode:
470 last = fnode
470 last = fnode
471
471
472 yield {"parity": parity.next(),
472 yield {"parity": parity.next(),
473 "node": hex(f.node()),
473 "node": hex(f.node()),
474 "rev": f.rev(),
474 "rev": f.rev(),
475 "author": name,
475 "author": name,
476 "file": f.path(),
476 "file": f.path(),
477 "targetline": targetline,
477 "targetline": targetline,
478 "line": l,
478 "line": l,
479 "lineid": "l%d" % (lineno + 1),
479 "lineid": "l%d" % (lineno + 1),
480 "linenumber": "% 6d" % (lineno + 1)}
480 "linenumber": "% 6d" % (lineno + 1)}
481
481
482 return tmpl("fileannotate",
482 return tmpl("fileannotate",
483 file=f,
483 file=f,
484 annotate=annotate,
484 annotate=annotate,
485 path=webutil.up(f),
485 path=webutil.up(f),
486 rev=fctx.rev(),
486 rev=fctx.rev(),
487 node=hex(fctx.node()),
487 node=hex(fctx.node()),
488 author=fctx.user(),
488 author=fctx.user(),
489 date=fctx.date(),
489 date=fctx.date(),
490 desc=fctx.description(),
490 desc=fctx.description(),
491 rename=webutil.renamelink(fctx),
491 rename=webutil.renamelink(fctx),
492 branch=webutil.nodebranchnodefault(fctx),
492 branch=webutil.nodebranchnodefault(fctx),
493 parent=webutil.siblings(fctx.parents()),
493 parent=webutil.siblings(fctx.parents()),
494 child=webutil.siblings(fctx.children()),
494 child=webutil.siblings(fctx.children()),
495 permissions=fctx.manifest().flags(f))
495 permissions=fctx.manifest().flags(f))
496
496
497 def filelog(web, req, tmpl):
497 def filelog(web, req, tmpl):
498 fctx = webutil.filectx(web.repo, req)
498 fctx = webutil.filectx(web.repo, req)
499 f = fctx.path()
499 f = fctx.path()
500 fl = fctx.filelog()
500 fl = fctx.filelog()
501 count = fl.count()
501 count = fl.count()
502 pagelen = web.maxshortchanges
502 pagelen = web.maxshortchanges
503 pos = fctx.filerev()
503 pos = fctx.filerev()
504 start = max(0, pos - pagelen + 1)
504 start = max(0, pos - pagelen + 1)
505 end = min(count, start + pagelen)
505 end = min(count, start + pagelen)
506 pos = end - 1
506 pos = end - 1
507 parity = paritygen(web.stripecount, offset=start-end)
507 parity = paritygen(web.stripecount, offset=start-end)
508
508
509 def entries(limit=0, **map):
509 def entries(limit=0, **map):
510 l = []
510 l = []
511
511
512 for i in xrange(start, end):
512 for i in xrange(start, end):
513 ctx = fctx.filectx(i)
513 ctx = fctx.filectx(i)
514 n = fl.node(i)
514 n = fl.node(i)
515
515
516 l.insert(0, {"parity": parity.next(),
516 l.insert(0, {"parity": parity.next(),
517 "filerev": i,
517 "filerev": i,
518 "file": f,
518 "file": f,
519 "node": hex(ctx.node()),
519 "node": hex(ctx.node()),
520 "author": ctx.user(),
520 "author": ctx.user(),
521 "date": ctx.date(),
521 "date": ctx.date(),
522 "rename": webutil.renamelink(fctx),
522 "rename": webutil.renamelink(fctx),
523 "parent": webutil.siblings(fctx.parents()),
523 "parent": webutil.siblings(fctx.parents()),
524 "child": webutil.siblings(fctx.children()),
524 "child": webutil.siblings(fctx.children()),
525 "desc": ctx.description()})
525 "desc": ctx.description()})
526
526
527 if limit > 0:
527 if limit > 0:
528 l = l[:limit]
528 l = l[:limit]
529
529
530 for e in l:
530 for e in l:
531 yield e
531 yield e
532
532
533 nodefunc = lambda x: fctx.filectx(fileid=x)
533 nodefunc = lambda x: fctx.filectx(fileid=x)
534 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
534 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
535 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
535 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
536 entries=lambda **x: entries(limit=0, **x),
536 entries=lambda **x: entries(limit=0, **x),
537 latestentry=lambda **x: entries(limit=1, **x))
537 latestentry=lambda **x: entries(limit=1, **x))
538
538
539
539
540 def archive(web, req, tmpl):
540 def archive(web, req, tmpl):
541 type_ = req.form['type'][0]
541 type_ = req.form['type'][0]
542 allowed = web.configlist("web", "allow_archive")
542 allowed = web.configlist("web", "allow_archive")
543 key = req.form['node'][0]
543 key = req.form['node'][0]
544
544
545 if not (type_ in web.archives and (type_ in allowed or
545 if not (type_ in web.archives and (type_ in allowed or
546 web.configbool("web", "allow" + type_, False))):
546 web.configbool("web", "allow" + type_, False))):
547 msg = 'Unsupported archive type: %s' % type_
547 msg = 'Unsupported archive type: %s' % type_
548 raise ErrorResponse(HTTP_NOT_FOUND, msg)
548 raise ErrorResponse(HTTP_NOT_FOUND, msg)
549
549
550 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
550 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
551 cnode = web.repo.lookup(key)
551 cnode = web.repo.lookup(key)
552 arch_version = key
552 arch_version = key
553 if cnode == key or key == 'tip':
553 if cnode == key or key == 'tip':
554 arch_version = short(cnode)
554 arch_version = short(cnode)
555 name = "%s-%s" % (reponame, arch_version)
555 name = "%s-%s" % (reponame, arch_version)
556 mimetype, artype, extension, encoding = web.archive_specs[type_]
556 mimetype, artype, extension, encoding = web.archive_specs[type_]
557 headers = [
557 headers = [
558 ('Content-Type', mimetype),
558 ('Content-Type', mimetype),
559 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
559 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
560 ]
560 ]
561 if encoding:
561 if encoding:
562 headers.append(('Content-Encoding', encoding))
562 headers.append(('Content-Encoding', encoding))
563 req.header(headers)
563 req.header(headers)
564 req.respond(HTTP_OK)
564 req.respond(HTTP_OK)
565 archival.archive(web.repo, req, cnode, artype, prefix=name)
565 archival.archive(web.repo, req, cnode, artype, prefix=name)
566 return []
566 return []
567
567
568
568
569 def static(web, req, tmpl):
569 def static(web, req, tmpl):
570 fname = req.form['file'][0]
570 fname = req.form['file'][0]
571 # a repo owner may set web.static in .hg/hgrc to get any file
571 # a repo owner may set web.static in .hg/hgrc to get any file
572 # readable by the user running the CGI script
572 # readable by the user running the CGI script
573 static = web.config("web", "static",
573 static = web.config("web", "static",
574 os.path.join(web.templatepath, "static"),
574 os.path.join(web.templatepath, "static"),
575 untrusted=False)
575 untrusted=False)
576 return [staticfile(static, fname, req)]
576 return [staticfile(static, fname, req)]
@@ -1,143 +1,143
1 # hgweb/webutil.py - utility library for the web interface.
1 # hgweb/webutil.py - utility library for the web interface.
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-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 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
9 import os
10 from mercurial.node import hex, nullid
10 from mercurial.node import hex, nullid
11 from mercurial.repo import RepoError
11 from mercurial.repo import RepoError
12 from mercurial import util
12 from mercurial import util
13
13
14 def up(p):
14 def up(p):
15 if p[0] != "/":
15 if p[0] != "/":
16 p = "/" + p
16 p = "/" + p
17 if p[-1] == "/":
17 if p[-1] == "/":
18 p = p[:-1]
18 p = p[:-1]
19 up = os.path.dirname(p)
19 up = os.path.dirname(p)
20 if up == "/":
20 if up == "/":
21 return "/"
21 return "/"
22 return up + "/"
22 return up + "/"
23
23
24 def revnavgen(pos, pagelen, limit, nodefunc):
24 def revnavgen(pos, pagelen, limit, nodefunc):
25 def seq(factor, limit=None):
25 def seq(factor, limit=None):
26 if limit:
26 if limit:
27 yield limit
27 yield limit
28 if limit >= 20 and limit <= 40:
28 if limit >= 20 and limit <= 40:
29 yield 50
29 yield 50
30 else:
30 else:
31 yield 1 * factor
31 yield 1 * factor
32 yield 3 * factor
32 yield 3 * factor
33 for f in seq(factor * 10):
33 for f in seq(factor * 10):
34 yield f
34 yield f
35
35
36 def nav(**map):
36 def nav(**map):
37 l = []
37 l = []
38 last = 0
38 last = 0
39 for f in seq(1, pagelen):
39 for f in seq(1, pagelen):
40 if f < pagelen or f <= last:
40 if f < pagelen or f <= last:
41 continue
41 continue
42 if f > limit:
42 if f > limit:
43 break
43 break
44 last = f
44 last = f
45 if pos + f < limit:
45 if pos + f < limit:
46 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
46 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
47 if pos - f >= 0:
47 if pos - f >= 0:
48 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
48 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
49
49
50 try:
50 try:
51 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
51 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
52
52
53 for label, node in l:
53 for label, node in l:
54 yield {"label": label, "node": node}
54 yield {"label": label, "node": node}
55
55
56 yield {"label": "tip", "node": "tip"}
56 yield {"label": "tip", "node": "tip"}
57 except RepoError:
57 except RepoError:
58 pass
58 pass
59
59
60 return nav
60 return nav
61
61
62 def siblings(siblings=[], hiderev=None, **args):
62 def siblings(siblings=[], hiderev=None, **args):
63 siblings = [s for s in siblings if s.node() != nullid]
63 siblings = [s for s in siblings if s.node() != nullid]
64 if len(siblings) == 1 and siblings[0].rev() == hiderev:
64 if len(siblings) == 1 and siblings[0].rev() == hiderev:
65 return
65 return
66 for s in siblings:
66 for s in siblings:
67 d = {'node': hex(s.node()), 'rev': s.rev()}
67 d = {'node': hex(s.node()), 'rev': s.rev()}
68 if hasattr(s, 'path'):
68 if hasattr(s, 'path'):
69 d['file'] = s.path()
69 d['file'] = s.path()
70 d.update(args)
70 d.update(args)
71 yield d
71 yield d
72
72
73 def renamelink(fctx):
73 def renamelink(fctx):
74 r = fctx.renamed(node)
74 r = fctx.renamed()
75 if r:
75 if r:
76 return [dict(file=r[0], node=hex(r[1]))]
76 return [dict(file=r[0], node=hex(r[1]))]
77 return []
77 return []
78
78
79 def nodetagsdict(repo, node):
79 def nodetagsdict(repo, node):
80 return [{"name": i} for i in repo.nodetags(node)]
80 return [{"name": i} for i in repo.nodetags(node)]
81
81
82 def nodebranchdict(repo, ctx):
82 def nodebranchdict(repo, ctx):
83 branches = []
83 branches = []
84 branch = ctx.branch()
84 branch = ctx.branch()
85 # If this is an empty repo, ctx.node() == nullid,
85 # If this is an empty repo, ctx.node() == nullid,
86 # ctx.branch() == 'default', but branchtags() is
86 # ctx.branch() == 'default', but branchtags() is
87 # an empty dict. Using dict.get avoids a traceback.
87 # an empty dict. Using dict.get avoids a traceback.
88 if repo.branchtags().get(branch) == ctx.node():
88 if repo.branchtags().get(branch) == ctx.node():
89 branches.append({"name": branch})
89 branches.append({"name": branch})
90 return branches
90 return branches
91
91
92 def nodeinbranch(repo, ctx):
92 def nodeinbranch(repo, ctx):
93 branches = []
93 branches = []
94 branch = ctx.branch()
94 branch = ctx.branch()
95 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
95 if branch != 'default' and repo.branchtags().get(branch) != ctx.node():
96 branches.append({"name": branch})
96 branches.append({"name": branch})
97 return branches
97 return branches
98
98
99 def nodebranchnodefault(ctx):
99 def nodebranchnodefault(ctx):
100 branches = []
100 branches = []
101 branch = ctx.branch()
101 branch = ctx.branch()
102 if branch != 'default':
102 if branch != 'default':
103 branches.append({"name": branch})
103 branches.append({"name": branch})
104 return branches
104 return branches
105
105
106 def showtag(repo, tmpl, t1, node=nullid, **args):
106 def showtag(repo, tmpl, t1, node=nullid, **args):
107 for t in repo.nodetags(node):
107 for t in repo.nodetags(node):
108 yield tmpl(t1, tag=t, **args)
108 yield tmpl(t1, tag=t, **args)
109
109
110 def cleanpath(repo, path):
110 def cleanpath(repo, path):
111 path = path.lstrip('/')
111 path = path.lstrip('/')
112 return util.canonpath(repo.root, '', path)
112 return util.canonpath(repo.root, '', path)
113
113
114 def changectx(repo, req):
114 def changectx(repo, req):
115 if 'node' in req.form:
115 if 'node' in req.form:
116 changeid = req.form['node'][0]
116 changeid = req.form['node'][0]
117 elif 'manifest' in req.form:
117 elif 'manifest' in req.form:
118 changeid = req.form['manifest'][0]
118 changeid = req.form['manifest'][0]
119 else:
119 else:
120 changeid = repo.changelog.count() - 1
120 changeid = repo.changelog.count() - 1
121
121
122 try:
122 try:
123 ctx = repo.changectx(changeid)
123 ctx = repo.changectx(changeid)
124 except RepoError:
124 except RepoError:
125 man = repo.manifest
125 man = repo.manifest
126 mn = man.lookup(changeid)
126 mn = man.lookup(changeid)
127 ctx = repo.changectx(man.linkrev(mn))
127 ctx = repo.changectx(man.linkrev(mn))
128
128
129 return ctx
129 return ctx
130
130
131 def filectx(repo, req):
131 def filectx(repo, req):
132 path = cleanpath(repo, req.form['file'][0])
132 path = cleanpath(repo, req.form['file'][0])
133 if 'node' in req.form:
133 if 'node' in req.form:
134 changeid = req.form['node'][0]
134 changeid = req.form['node'][0]
135 else:
135 else:
136 changeid = req.form['filenode'][0]
136 changeid = req.form['filenode'][0]
137 try:
137 try:
138 ctx = repo.changectx(changeid)
138 ctx = repo.changectx(changeid)
139 fctx = ctx.filectx(path)
139 fctx = ctx.filectx(path)
140 except RepoError:
140 except RepoError:
141 fctx = repo.filectx(path, fileid=changeid)
141 fctx = repo.filectx(path, fileid=changeid)
142
142
143 return fctx
143 return fctx
General Comments 0
You need to be logged in to leave comments. Login now