##// END OF EJS Templates
hgweb: fix problems with empty repositories
Dirkjan Ochtman -
r7565:5f162f61 default
parent child Browse files
Show More
@@ -0,0 +1,12 b''
1 #!/bin/sh
2 # Some tests for hgweb in an empty repository
3
4 hg init test
5 cd test
6 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
7 cat hg.pid >> $DAEMON_PIDS
8
9 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/shortlog')
10 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log')
11 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/graph')
12 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file')
@@ -0,0 +1,348 b''
1 200 Script output follows
2
3 <!-- quirksmode -->
4 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
5 <html>
6 <head>
7 <link rel="icon" href="/static/hgicon.png" type="image/png">
8 <meta name="robots" content="index, nofollow" />
9 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
10
11 <title>test: log</title>
12 <link rel="alternate" type="application/atom+xml"
13 href="/atom-log" title="Atom feed for test">
14 <link rel="alternate" type="application/rss+xml"
15 href="/rss-log" title="RSS feed for test">
16 </head>
17 <body>
18
19 <div class="container">
20 <div class="menu">
21 <div class="logo">
22 <a href="http://www.selenic.com/mercurial/">
23 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
24 </div>
25 <ul>
26 <li class="active">log</li>
27 <li><a href="/graph/000000000000">graph</a></li>
28 <li><a href="/tags">tags</a></li>
29 </ul>
30 <ul>
31 <li><a href="/rev/000000000000">changeset</a></li>
32 <li><a href="/file/000000000000">browse</a></li>
33 </ul>
34 <ul>
35
36 </ul>
37 </div>
38
39 <div class="main">
40 <h2><a href="/">test</a></h2>
41 <h3>log</h3>
42
43 <form class="search" action="/log">
44
45 <p><input name="rev" id="search1" type="text" size="30"></p>
46 <span>find changesets by author, revision,
47 files, or words in the commit message</span>
48 </form>
49
50 <div class="navigate">rev -1: <a href="/shortlog/000000000000">(0)</a> <a href="/shortlog/tip">tip</a> </div>
51
52 <table class="bigtable">
53 <tr>
54 <th class="age">age</th>
55 <th class="author">author</th>
56 <th class="description">description</th>
57 </tr>
58
59 </table>
60
61 <div class="navigate">rev -1: <a href="/shortlog/000000000000">(0)</a> <a href="/shortlog/tip">tip</a> </div>
62 </div>
63 </div>
64
65
66
67 </body>
68 </html>
69
70 200 Script output follows
71
72 <!-- quirksmode -->
73 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
74 <html>
75 <head>
76 <link rel="icon" href="/static/hgicon.png" type="image/png">
77 <meta name="robots" content="index, nofollow" />
78 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
79
80 <title>test: log</title>
81 <link rel="alternate" type="application/atom+xml"
82 href="/atom-log" title="Atom feed for test">
83 <link rel="alternate" type="application/rss+xml"
84 href="/rss-log" title="RSS feed for test">
85 </head>
86 <body>
87
88 <div class="container">
89 <div class="menu">
90 <div class="logo">
91 <a href="http://www.selenic.com/mercurial/">
92 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
93 </div>
94 <ul>
95 <li class="active">log</li>
96 <li><a href="/graph/000000000000">graph</a></li>
97 <li><a href="/tags">tags</a></li>
98 </ul>
99 <ul>
100 <li><a href="/rev/000000000000">changeset</a></li>
101 <li><a href="/file/000000000000">browse</a></li>
102 </ul>
103 <ul>
104
105 </ul>
106 </div>
107
108 <div class="main">
109 <h2><a href="/">test</a></h2>
110 <h3>log</h3>
111
112 <form class="search" action="/log">
113
114 <p><input name="rev" id="search1" type="text" size="30"></p>
115 <span>find changesets by author, revision,
116 files, or words in the commit message</span>
117 </form>
118
119 <div class="navigate">rev -1: <a href="/shortlog/000000000000">(0)</a> <a href="/shortlog/tip">tip</a> </div>
120
121 <table class="bigtable">
122 <tr>
123 <th class="age">age</th>
124 <th class="author">author</th>
125 <th class="description">description</th>
126 </tr>
127
128 </table>
129
130 <div class="navigate">rev -1: <a href="/shortlog/000000000000">(0)</a> <a href="/shortlog/tip">tip</a> </div>
131 </div>
132 </div>
133
134
135
136 </body>
137 </html>
138
139 200 Script output follows
140
141 <!-- quirksmode -->
142 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
143 <html>
144 <head>
145 <link rel="icon" href="/static/hgicon.png" type="image/png">
146 <meta name="robots" content="index, nofollow" />
147 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
148
149 <title>test: revision graph</title>
150 <link rel="alternate" type="application/atom+xml"
151 href="/atom-log" title="Atom feed for test: log">
152 <link rel="alternate" type="application/rss+xml"
153 href="/rss-log" title="RSS feed for test: log">
154 <!--[if IE]><script type="text/javascript" src="/static/excanvas.js"></script><![endif]-->
155 </head>
156 <body>
157
158 <div class="container">
159 <div class="menu">
160 <div class="logo">
161 <a href="http://www.selenic.com/mercurial/">
162 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
163 </div>
164 <ul>
165 <li><a href="/shortlog/000000000000">log</a></li>
166 <li class="active">graph</li>
167 <li><a href="/tags">tags</a></li>
168 </ul>
169 <ul>
170 <li><a href="/rev/000000000000">changeset</a></li>
171 <li><a href="/file/000000000000">browse</a></li>
172 </ul>
173 </div>
174
175 <div class="main">
176 <h2><a href="/">test</a></h2>
177 <h3>graph</h3>
178
179 <form class="search" action="/log">
180
181 <p><input name="rev" id="search1" type="text" size="30"></p>
182 <span>find changesets by author, revision,
183 files, or words in the commit message</span>
184 </form>
185
186 <div class="navigate">
187 <a href="/graph/-1?revcount=12">less</a>
188 <a href="/graph/-1?revcount=50">more</a>
189 | rev -1: <a href="/graph/000000000000">(0)</a> <a href="/graph/tip">tip</a>
190 </div>
191
192 <noscript>The revision graph only works with JavaScript-enabled browsers.</noscript>
193
194 <div id="wrapper">
195 <ul id="nodebgs"></ul>
196 <canvas id="graph" width="224" height="12"></canvas>
197 <ul id="graphnodes"></ul>
198 </div>
199
200 <script type="text/javascript" src="/static/graph.js"></script>
201 <script type="text/javascript">
202 <!-- hide script content
203
204 var data = [];
205 var graph = new Graph();
206 graph.scale(39);
207
208 graph.edge = function(x0, y0, x1, y1, color) {
209
210 this.setColor(color, 0.0, 0.65);
211 this.ctx.beginPath();
212 this.ctx.moveTo(x0, y0);
213 this.ctx.lineTo(x1, y1);
214 this.ctx.stroke();
215
216 }
217
218 var revlink = '<li style="_STYLE"><span class="desc">';
219 revlink += '<a href="/rev/_NODEID" title="_NODEID">_DESC</a>';
220 revlink += '</span>_TAGS<span class="info">_DATE ago, by _USER</span></li>';
221
222 graph.vertex = function(x, y, color, parity, cur) {
223
224 this.ctx.beginPath();
225 color = this.setColor(color, 0.25, 0.75);
226 this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
227 this.ctx.fill();
228
229 var bg = '<li class="bg parity' + parity + '"></li>';
230 var left = (this.columns + 1) * this.bg_height;
231 var nstyle = 'padding-left: ' + left + 'px;';
232 var item = revlink.replace(/_STYLE/, nstyle);
233 item = item.replace(/_PARITY/, 'parity' + parity);
234 item = item.replace(/_NODEID/, cur[0]);
235 item = item.replace(/_NODEID/, cur[0]);
236 item = item.replace(/_DESC/, cur[3]);
237 item = item.replace(/_USER/, cur[4]);
238 item = item.replace(/_DATE/, cur[5]);
239
240 var tagspan = '';
241 if (cur[7].length || (cur[6][0] != 'default' || cur[6][1])) {
242 tagspan = '<span class="logtags">';
243 if (cur[6][1]) {
244 tagspan += '<span class="branchhead" title="' + cur[6][0] + '">';
245 tagspan += cur[6][0] + '</span> ';
246 } else if (!cur[6][1] && cur[6][0] != 'default') {
247 tagspan += '<span class="branchname" title="' + cur[6][0] + '">';
248 tagspan += cur[6][0] + '</span> ';
249 }
250 if (cur[7].length) {
251 for (var t in cur[7]) {
252 var tag = cur[7][t];
253 tagspan += '<span class="tag">' + tag + '</span> ';
254 }
255 }
256 tagspan += '</span>';
257 }
258
259 item = item.replace(/_TAGS/, tagspan);
260 return [bg, item];
261
262 }
263
264 graph.render(data);
265
266 // stop hiding script -->
267 </script>
268
269 <div class="navigate">
270 <a href="/graph/-1?revcount=12">less</a>
271 <a href="/graph/-1?revcount=50">more</a>
272 | rev -1: <a href="/graph/000000000000">(0)</a> <a href="/graph/tip">tip</a>
273 </div>
274
275 </div>
276 </div>
277
278
279
280 </body>
281 </html>
282
283 200 Script output follows
284
285 <!-- quirksmode -->
286 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
287 <html>
288 <head>
289 <link rel="icon" href="/static/hgicon.png" type="image/png">
290 <meta name="robots" content="index, nofollow" />
291 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
292
293 <title>test: 000000000000 /</title>
294 </head>
295 <body>
296
297 <div class="container">
298 <div class="menu">
299 <div class="logo">
300 <a href="http://www.selenic.com/mercurial/">
301 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
302 </div>
303 <ul>
304 <li><a href="/shortlog/000000000000">log</a></li>
305 <li><a href="/graph/000000000000">graph</a></li>
306 <li><a href="/tags">tags</a></li>
307 </ul>
308 <ul>
309 <li><a href="/rev/000000000000">changeset</a></li>
310 <li class="active">browse</li>
311 </ul>
312 <ul>
313
314 </ul>
315 </div>
316
317 <div class="main">
318 <h2><a href="/">test</a></h2>
319 <h3>directory / @ -1:000000000000 <span class="tag">tip</span> </h3>
320
321 <form class="search" action="/log">
322
323 <p><input name="rev" id="search1" type="text" size="30"></p>
324 <span>find changesets by author, revision,
325 files, or words in the commit message</span>
326 </form>
327
328 <table class="bigtable">
329 <tr>
330 <th class="name">name</th>
331 <th class="size">size</th>
332 <th class="permissions">permissions</th>
333 </tr>
334 <tr class="fileline parity0">
335 <td class="name"><a href="/file/000000000000/">[up]</a></td>
336 <td class="size"></td>
337 <td class="permissions">drwxr-xr-x</td>
338 </tr>
339
340
341 </table>
342 </div>
343 </div>
344
345
346 </body>
347 </html>
348
@@ -1,73 +1,76 b''
1 # Revision graph generator for Mercurial
1 # Revision graph generator for Mercurial
2 #
2 #
3 # Copyright 2008 Dirkjan Ochtman <dirkjan@ochtman.nl>
3 # Copyright 2008 Dirkjan Ochtman <dirkjan@ochtman.nl>
4 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
4 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
5 #
5 #
6 # This software may be used and distributed according to the terms of
6 # This software may be used and distributed according to the terms of
7 # the GNU General Public License, incorporated herein by reference.
7 # the GNU General Public License, incorporated herein by reference.
8
8
9 from node import nullrev, short
9 from node import nullrev, short
10 import ui, hg, util, templatefilters
10 import ui, hg, util, templatefilters
11
11
12 def graph(repo, start_rev, stop_rev):
12 def graph(repo, start_rev, stop_rev):
13 """incremental revision grapher
13 """incremental revision grapher
14
14
15 This generator function walks through the revision history from
15 This generator function walks through the revision history from
16 revision start_rev to revision stop_rev (which must be less than
16 revision start_rev to revision stop_rev (which must be less than
17 or equal to start_rev) and for each revision emits tuples with the
17 or equal to start_rev) and for each revision emits tuples with the
18 following elements:
18 following elements:
19
19
20 - Current node
20 - Current node
21 - Column and color for the current node
21 - Column and color for the current node
22 - Edges; a list of (col, next_col, color) indicating the edges between
22 - Edges; a list of (col, next_col, color) indicating the edges between
23 the current node and its parents.
23 the current node and its parents.
24 - First line of the changeset description
24 - First line of the changeset description
25 - The changeset author
25 - The changeset author
26 - The changeset date/time
26 - The changeset date/time
27 """
27 """
28
28
29 if start_rev == nullrev and not stop_rev:
30 return
31
29 assert start_rev >= stop_rev
32 assert start_rev >= stop_rev
30 assert stop_rev >= 0
33 assert stop_rev >= 0
31 curr_rev = start_rev
34 curr_rev = start_rev
32 revs = []
35 revs = []
33 cl = repo.changelog
36 cl = repo.changelog
34 colors = {}
37 colors = {}
35 new_color = 1
38 new_color = 1
36
39
37 while curr_rev >= stop_rev:
40 while curr_rev >= stop_rev:
38 # Compute revs and next_revs
41 # Compute revs and next_revs
39 if curr_rev not in revs:
42 if curr_rev not in revs:
40 revs.append(curr_rev) # new head
43 revs.append(curr_rev) # new head
41 colors[curr_rev] = new_color
44 colors[curr_rev] = new_color
42 new_color += 1
45 new_color += 1
43
46
44 idx = revs.index(curr_rev)
47 idx = revs.index(curr_rev)
45 color = colors.pop(curr_rev)
48 color = colors.pop(curr_rev)
46 next = revs[:]
49 next = revs[:]
47
50
48 # Add parents to next_revs
51 # Add parents to next_revs
49 parents = [x for x in cl.parentrevs(curr_rev) if x != nullrev]
52 parents = [x for x in cl.parentrevs(curr_rev) if x != nullrev]
50 addparents = [p for p in parents if p not in next]
53 addparents = [p for p in parents if p not in next]
51 next[idx:idx + 1] = addparents
54 next[idx:idx + 1] = addparents
52
55
53 # Set colors for the parents
56 # Set colors for the parents
54 for i, p in enumerate(addparents):
57 for i, p in enumerate(addparents):
55 if not i:
58 if not i:
56 colors[p] = color
59 colors[p] = color
57 else:
60 else:
58 colors[p] = new_color
61 colors[p] = new_color
59 new_color += 1
62 new_color += 1
60
63
61 # Add edges to the graph
64 # Add edges to the graph
62 edges = []
65 edges = []
63 for col, r in enumerate(revs):
66 for col, r in enumerate(revs):
64 if r in next:
67 if r in next:
65 edges.append((col, next.index(r), colors[r]))
68 edges.append((col, next.index(r), colors[r]))
66 elif r == curr_rev:
69 elif r == curr_rev:
67 for p in parents:
70 for p in parents:
68 edges.append((col, next.index(p), colors[p]))
71 edges.append((col, next.index(p), colors[p]))
69
72
70 # Yield and move on
73 # Yield and move on
71 yield (repo[curr_rev], (idx, color), edges)
74 yield (repo[curr_rev], (idx, color), edges)
72 revs = next
75 revs = next
73 curr_rev -= 1
76 curr_rev -= 1
@@ -1,663 +1,663 b''
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, cgi, copy
8 import os, mimetypes, re, cgi, copy
9 import webutil
9 import webutil
10 from mercurial import revlog, archival, templatefilters
10 from mercurial import revlog, archival, templatefilters
11 from mercurial.node import short, hex, nullid
11 from mercurial.node import short, hex, nullid
12 from mercurial.util import binary, datestr
12 from mercurial.util import binary, datestr
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_FORBIDDEN, HTTP_NOT_FOUND
15 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
16 from mercurial import graphmod, util
16 from mercurial import graphmod, util
17
17
18 # __all__ is populated with the allowed commands. Be sure to add to it if
18 # __all__ is populated with the allowed commands. Be sure to add to it if
19 # you're adding a new command, or the new command won't work.
19 # you're adding a new command, or the new command won't work.
20
20
21 __all__ = [
21 __all__ = [
22 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
22 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
23 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog',
23 'manifest', 'tags', 'summary', 'filediff', 'diff', 'annotate', 'filelog',
24 'archive', 'static', 'graph',
24 'archive', 'static', 'graph',
25 ]
25 ]
26
26
27 def log(web, req, tmpl):
27 def log(web, req, tmpl):
28 if 'file' in req.form and req.form['file'][0]:
28 if 'file' in req.form and req.form['file'][0]:
29 return filelog(web, req, tmpl)
29 return filelog(web, req, tmpl)
30 else:
30 else:
31 return changelog(web, req, tmpl)
31 return changelog(web, req, tmpl)
32
32
33 def rawfile(web, req, tmpl):
33 def rawfile(web, req, tmpl):
34 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
34 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
35 if not path:
35 if not path:
36 content = manifest(web, req, tmpl)
36 content = manifest(web, req, tmpl)
37 req.respond(HTTP_OK, web.ctype)
37 req.respond(HTTP_OK, web.ctype)
38 return content
38 return content
39
39
40 try:
40 try:
41 fctx = webutil.filectx(web.repo, req)
41 fctx = webutil.filectx(web.repo, req)
42 except revlog.LookupError, inst:
42 except revlog.LookupError, inst:
43 try:
43 try:
44 content = manifest(web, req, tmpl)
44 content = manifest(web, req, tmpl)
45 req.respond(HTTP_OK, web.ctype)
45 req.respond(HTTP_OK, web.ctype)
46 return content
46 return content
47 except ErrorResponse:
47 except ErrorResponse:
48 raise inst
48 raise inst
49
49
50 path = fctx.path()
50 path = fctx.path()
51 text = fctx.data()
51 text = fctx.data()
52 mt = mimetypes.guess_type(path)[0]
52 mt = mimetypes.guess_type(path)[0]
53 if mt is None:
53 if mt is None:
54 mt = binary(text) and 'application/octet-stream' or 'text/plain'
54 mt = binary(text) and 'application/octet-stream' or 'text/plain'
55
55
56 req.respond(HTTP_OK, mt, path, len(text))
56 req.respond(HTTP_OK, mt, path, len(text))
57 return [text]
57 return [text]
58
58
59 def _filerevision(web, tmpl, fctx):
59 def _filerevision(web, tmpl, fctx):
60 f = fctx.path()
60 f = fctx.path()
61 text = fctx.data()
61 text = fctx.data()
62 parity = paritygen(web.stripecount)
62 parity = paritygen(web.stripecount)
63
63
64 if binary(text):
64 if binary(text):
65 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
65 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
66 text = '(binary:%s)' % mt
66 text = '(binary:%s)' % mt
67
67
68 def lines():
68 def lines():
69 for lineno, t in enumerate(text.splitlines(1)):
69 for lineno, t in enumerate(text.splitlines(1)):
70 yield {"line": t,
70 yield {"line": t,
71 "lineid": "l%d" % (lineno + 1),
71 "lineid": "l%d" % (lineno + 1),
72 "linenumber": "% 6d" % (lineno + 1),
72 "linenumber": "% 6d" % (lineno + 1),
73 "parity": parity.next()}
73 "parity": parity.next()}
74
74
75 return tmpl("filerevision",
75 return tmpl("filerevision",
76 file=f,
76 file=f,
77 path=webutil.up(f),
77 path=webutil.up(f),
78 text=lines(),
78 text=lines(),
79 rev=fctx.rev(),
79 rev=fctx.rev(),
80 node=hex(fctx.node()),
80 node=hex(fctx.node()),
81 author=fctx.user(),
81 author=fctx.user(),
82 date=fctx.date(),
82 date=fctx.date(),
83 desc=fctx.description(),
83 desc=fctx.description(),
84 branch=webutil.nodebranchnodefault(fctx),
84 branch=webutil.nodebranchnodefault(fctx),
85 parent=webutil.siblings(fctx.parents()),
85 parent=webutil.siblings(fctx.parents()),
86 child=webutil.siblings(fctx.children()),
86 child=webutil.siblings(fctx.children()),
87 rename=webutil.renamelink(fctx),
87 rename=webutil.renamelink(fctx),
88 permissions=fctx.manifest().flags(f))
88 permissions=fctx.manifest().flags(f))
89
89
90 def file(web, req, tmpl):
90 def file(web, req, tmpl):
91 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
91 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
92 if not path:
92 if not path:
93 return manifest(web, req, tmpl)
93 return manifest(web, req, tmpl)
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 try:
97 try:
98 return manifest(web, req, tmpl)
98 return manifest(web, req, tmpl)
99 except ErrorResponse:
99 except ErrorResponse:
100 raise inst
100 raise inst
101
101
102 def _search(web, tmpl, query):
102 def _search(web, tmpl, query):
103
103
104 def changelist(**map):
104 def changelist(**map):
105 cl = web.repo.changelog
105 cl = web.repo.changelog
106 count = 0
106 count = 0
107 qw = query.lower().split()
107 qw = query.lower().split()
108
108
109 def revgen():
109 def revgen():
110 for i in xrange(len(cl) - 1, 0, -100):
110 for i in xrange(len(cl) - 1, 0, -100):
111 l = []
111 l = []
112 for j in xrange(max(0, i - 100), i + 1):
112 for j in xrange(max(0, i - 100), i + 1):
113 ctx = web.repo[j]
113 ctx = web.repo[j]
114 l.append(ctx)
114 l.append(ctx)
115 l.reverse()
115 l.reverse()
116 for e in l:
116 for e in l:
117 yield e
117 yield e
118
118
119 for ctx in revgen():
119 for ctx in revgen():
120 miss = 0
120 miss = 0
121 for q in qw:
121 for q in qw:
122 if not (q in ctx.user().lower() or
122 if not (q in ctx.user().lower() or
123 q in ctx.description().lower() or
123 q in ctx.description().lower() or
124 q in " ".join(ctx.files()).lower()):
124 q in " ".join(ctx.files()).lower()):
125 miss = 1
125 miss = 1
126 break
126 break
127 if miss:
127 if miss:
128 continue
128 continue
129
129
130 count += 1
130 count += 1
131 n = ctx.node()
131 n = ctx.node()
132 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
132 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
133 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
133 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
134
134
135 yield tmpl('searchentry',
135 yield tmpl('searchentry',
136 parity=parity.next(),
136 parity=parity.next(),
137 author=ctx.user(),
137 author=ctx.user(),
138 parent=webutil.siblings(ctx.parents()),
138 parent=webutil.siblings(ctx.parents()),
139 child=webutil.siblings(ctx.children()),
139 child=webutil.siblings(ctx.children()),
140 changelogtag=showtags,
140 changelogtag=showtags,
141 desc=ctx.description(),
141 desc=ctx.description(),
142 date=ctx.date(),
142 date=ctx.date(),
143 files=files,
143 files=files,
144 rev=ctx.rev(),
144 rev=ctx.rev(),
145 node=hex(n),
145 node=hex(n),
146 tags=webutil.nodetagsdict(web.repo, n),
146 tags=webutil.nodetagsdict(web.repo, n),
147 inbranch=webutil.nodeinbranch(web.repo, ctx),
147 inbranch=webutil.nodeinbranch(web.repo, ctx),
148 branches=webutil.nodebranchdict(web.repo, ctx))
148 branches=webutil.nodebranchdict(web.repo, ctx))
149
149
150 if count >= web.maxchanges:
150 if count >= web.maxchanges:
151 break
151 break
152
152
153 cl = web.repo.changelog
153 cl = web.repo.changelog
154 parity = paritygen(web.stripecount)
154 parity = paritygen(web.stripecount)
155
155
156 return tmpl('search',
156 return tmpl('search',
157 query=query,
157 query=query,
158 node=hex(cl.tip()),
158 node=hex(cl.tip()),
159 entries=changelist,
159 entries=changelist,
160 archives=web.archivelist("tip"))
160 archives=web.archivelist("tip"))
161
161
162 def changelog(web, req, tmpl, shortlog = False):
162 def changelog(web, req, tmpl, shortlog = False):
163 if 'node' in req.form:
163 if 'node' in req.form:
164 ctx = webutil.changectx(web.repo, req)
164 ctx = webutil.changectx(web.repo, req)
165 else:
165 else:
166 if 'rev' in req.form:
166 if 'rev' in req.form:
167 hi = req.form['rev'][0]
167 hi = req.form['rev'][0]
168 else:
168 else:
169 hi = len(web.repo) - 1
169 hi = len(web.repo) - 1
170 try:
170 try:
171 ctx = web.repo[hi]
171 ctx = web.repo[hi]
172 except RepoError:
172 except RepoError:
173 return _search(web, tmpl, hi) # XXX redirect to 404 page?
173 return _search(web, tmpl, hi) # XXX redirect to 404 page?
174
174
175 def changelist(limit=0, **map):
175 def changelist(limit=0, **map):
176 cl = web.repo.changelog
176 cl = web.repo.changelog
177 l = [] # build a list in forward order for efficiency
177 l = [] # build a list in forward order for efficiency
178 for i in xrange(start, end):
178 for i in xrange(start, end):
179 ctx = web.repo[i]
179 ctx = web.repo[i]
180 n = ctx.node()
180 n = ctx.node()
181 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
181 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
182 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
182 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
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": files,
191 "files": files,
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 = len(cl)
207 count = len(cl)
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 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
229 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
230 showbranch = webutil.nodebranchnodefault(ctx)
230 showbranch = webutil.nodebranchnodefault(ctx)
231 parents = ctx.parents()
231 parents = ctx.parents()
232
232
233 files = []
233 files = []
234 parity = paritygen(web.stripecount)
234 parity = paritygen(web.stripecount)
235 for f in ctx.files():
235 for f in ctx.files():
236 template = f in ctx and 'filenodelink' or 'filenolink'
236 template = f in ctx and 'filenodelink' or 'filenolink'
237 files.append(tmpl(template,
237 files.append(tmpl(template,
238 node=ctx.hex(), file=f,
238 node=ctx.hex(), file=f,
239 parity=parity.next()))
239 parity=parity.next()))
240
240
241 parity = paritygen(web.stripecount)
241 parity = paritygen(web.stripecount)
242 diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity)
242 diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity)
243 return tmpl('changeset',
243 return tmpl('changeset',
244 diff=diffs,
244 diff=diffs,
245 rev=ctx.rev(),
245 rev=ctx.rev(),
246 node=ctx.hex(),
246 node=ctx.hex(),
247 parent=webutil.siblings(parents),
247 parent=webutil.siblings(parents),
248 child=webutil.siblings(ctx.children()),
248 child=webutil.siblings(ctx.children()),
249 changesettag=showtags,
249 changesettag=showtags,
250 changesetbranch=showbranch,
250 changesetbranch=showbranch,
251 author=ctx.user(),
251 author=ctx.user(),
252 desc=ctx.description(),
252 desc=ctx.description(),
253 date=ctx.date(),
253 date=ctx.date(),
254 files=files,
254 files=files,
255 archives=web.archivelist(ctx.hex()),
255 archives=web.archivelist(ctx.hex()),
256 tags=webutil.nodetagsdict(web.repo, ctx.node()),
256 tags=webutil.nodetagsdict(web.repo, ctx.node()),
257 branch=webutil.nodebranchnodefault(ctx),
257 branch=webutil.nodebranchnodefault(ctx),
258 inbranch=webutil.nodeinbranch(web.repo, ctx),
258 inbranch=webutil.nodeinbranch(web.repo, ctx),
259 branches=webutil.nodebranchdict(web.repo, ctx))
259 branches=webutil.nodebranchdict(web.repo, ctx))
260
260
261 rev = changeset
261 rev = changeset
262
262
263 def manifest(web, req, tmpl):
263 def manifest(web, req, tmpl):
264 ctx = webutil.changectx(web.repo, req)
264 ctx = webutil.changectx(web.repo, req)
265 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
265 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
266 mf = ctx.manifest()
266 mf = ctx.manifest()
267 node = ctx.node()
267 node = ctx.node()
268
268
269 files = {}
269 files = {}
270 dirs = {}
270 dirs = {}
271 parity = paritygen(web.stripecount)
271 parity = paritygen(web.stripecount)
272
272
273 if path and path[-1] != "/":
273 if path and path[-1] != "/":
274 path += "/"
274 path += "/"
275 l = len(path)
275 l = len(path)
276 abspath = "/" + path
276 abspath = "/" + path
277
277
278 for f, n in mf.items():
278 for f, n in mf.items():
279 if f[:l] != path:
279 if f[:l] != path:
280 continue
280 continue
281 remain = f[l:]
281 remain = f[l:]
282 elements = remain.split('/')
282 elements = remain.split('/')
283 if len(elements) == 1:
283 if len(elements) == 1:
284 files[remain] = f
284 files[remain] = f
285 else:
285 else:
286 h = dirs # need to retain ref to dirs (root)
286 h = dirs # need to retain ref to dirs (root)
287 for elem in elements[0:-1]:
287 for elem in elements[0:-1]:
288 if elem not in h:
288 if elem not in h:
289 h[elem] = {}
289 h[elem] = {}
290 h = h[elem]
290 h = h[elem]
291 if len(h) > 1:
291 if len(h) > 1:
292 break
292 break
293 h[None] = None # denotes files present
293 h[None] = None # denotes files present
294
294
295 if not files and not dirs:
295 if mf and not files and not dirs:
296 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
296 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
297
297
298 def filelist(**map):
298 def filelist(**map):
299 for f in util.sort(files):
299 for f in util.sort(files):
300 full = files[f]
300 full = files[f]
301
301
302 fctx = ctx.filectx(full)
302 fctx = ctx.filectx(full)
303 yield {"file": full,
303 yield {"file": full,
304 "parity": parity.next(),
304 "parity": parity.next(),
305 "basename": f,
305 "basename": f,
306 "date": fctx.date(),
306 "date": fctx.date(),
307 "size": fctx.size(),
307 "size": fctx.size(),
308 "permissions": mf.flags(full)}
308 "permissions": mf.flags(full)}
309
309
310 def dirlist(**map):
310 def dirlist(**map):
311 for d in util.sort(dirs):
311 for d in util.sort(dirs):
312
312
313 emptydirs = []
313 emptydirs = []
314 h = dirs[d]
314 h = dirs[d]
315 while isinstance(h, dict) and len(h) == 1:
315 while isinstance(h, dict) and len(h) == 1:
316 k,v = h.items()[0]
316 k,v = h.items()[0]
317 if v:
317 if v:
318 emptydirs.append(k)
318 emptydirs.append(k)
319 h = v
319 h = v
320
320
321 path = "%s%s" % (abspath, d)
321 path = "%s%s" % (abspath, d)
322 yield {"parity": parity.next(),
322 yield {"parity": parity.next(),
323 "path": path,
323 "path": path,
324 "emptydirs": "/".join(emptydirs),
324 "emptydirs": "/".join(emptydirs),
325 "basename": d}
325 "basename": d}
326
326
327 return tmpl("manifest",
327 return tmpl("manifest",
328 rev=ctx.rev(),
328 rev=ctx.rev(),
329 node=hex(node),
329 node=hex(node),
330 path=abspath,
330 path=abspath,
331 up=webutil.up(abspath),
331 up=webutil.up(abspath),
332 upparity=parity.next(),
332 upparity=parity.next(),
333 fentries=filelist,
333 fentries=filelist,
334 dentries=dirlist,
334 dentries=dirlist,
335 archives=web.archivelist(hex(node)),
335 archives=web.archivelist(hex(node)),
336 tags=webutil.nodetagsdict(web.repo, node),
336 tags=webutil.nodetagsdict(web.repo, node),
337 inbranch=webutil.nodeinbranch(web.repo, ctx),
337 inbranch=webutil.nodeinbranch(web.repo, ctx),
338 branches=webutil.nodebranchdict(web.repo, ctx))
338 branches=webutil.nodebranchdict(web.repo, ctx))
339
339
340 def tags(web, req, tmpl):
340 def tags(web, req, tmpl):
341 i = web.repo.tagslist()
341 i = web.repo.tagslist()
342 i.reverse()
342 i.reverse()
343 parity = paritygen(web.stripecount)
343 parity = paritygen(web.stripecount)
344
344
345 def entries(notip=False,limit=0, **map):
345 def entries(notip=False,limit=0, **map):
346 count = 0
346 count = 0
347 for k, n in i:
347 for k, n in i:
348 if notip and k == "tip":
348 if notip and k == "tip":
349 continue
349 continue
350 if limit > 0 and count >= limit:
350 if limit > 0 and count >= limit:
351 continue
351 continue
352 count = count + 1
352 count = count + 1
353 yield {"parity": parity.next(),
353 yield {"parity": parity.next(),
354 "tag": k,
354 "tag": k,
355 "date": web.repo[n].date(),
355 "date": web.repo[n].date(),
356 "node": hex(n)}
356 "node": hex(n)}
357
357
358 return tmpl("tags",
358 return tmpl("tags",
359 node=hex(web.repo.changelog.tip()),
359 node=hex(web.repo.changelog.tip()),
360 entries=lambda **x: entries(False,0, **x),
360 entries=lambda **x: entries(False,0, **x),
361 entriesnotip=lambda **x: entries(True,0, **x),
361 entriesnotip=lambda **x: entries(True,0, **x),
362 latestentry=lambda **x: entries(True,1, **x))
362 latestentry=lambda **x: entries(True,1, **x))
363
363
364 def summary(web, req, tmpl):
364 def summary(web, req, tmpl):
365 i = web.repo.tagslist()
365 i = web.repo.tagslist()
366 i.reverse()
366 i.reverse()
367
367
368 def tagentries(**map):
368 def tagentries(**map):
369 parity = paritygen(web.stripecount)
369 parity = paritygen(web.stripecount)
370 count = 0
370 count = 0
371 for k, n in i:
371 for k, n in i:
372 if k == "tip": # skip tip
372 if k == "tip": # skip tip
373 continue
373 continue
374
374
375 count += 1
375 count += 1
376 if count > 10: # limit to 10 tags
376 if count > 10: # limit to 10 tags
377 break
377 break
378
378
379 yield tmpl("tagentry",
379 yield tmpl("tagentry",
380 parity=parity.next(),
380 parity=parity.next(),
381 tag=k,
381 tag=k,
382 node=hex(n),
382 node=hex(n),
383 date=web.repo[n].date())
383 date=web.repo[n].date())
384
384
385 def branches(**map):
385 def branches(**map):
386 parity = paritygen(web.stripecount)
386 parity = paritygen(web.stripecount)
387
387
388 b = web.repo.branchtags()
388 b = web.repo.branchtags()
389 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
389 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
390 for r,n,t in util.sort(l):
390 for r,n,t in util.sort(l):
391 yield {'parity': parity.next(),
391 yield {'parity': parity.next(),
392 'branch': t,
392 'branch': t,
393 'node': hex(n),
393 'node': hex(n),
394 'date': web.repo[n].date()}
394 'date': web.repo[n].date()}
395
395
396 def changelist(**map):
396 def changelist(**map):
397 parity = paritygen(web.stripecount, offset=start-end)
397 parity = paritygen(web.stripecount, offset=start-end)
398 l = [] # build a list in forward order for efficiency
398 l = [] # build a list in forward order for efficiency
399 for i in xrange(start, end):
399 for i in xrange(start, end):
400 ctx = web.repo[i]
400 ctx = web.repo[i]
401 n = ctx.node()
401 n = ctx.node()
402 hn = hex(n)
402 hn = hex(n)
403
403
404 l.insert(0, tmpl(
404 l.insert(0, tmpl(
405 'shortlogentry',
405 'shortlogentry',
406 parity=parity.next(),
406 parity=parity.next(),
407 author=ctx.user(),
407 author=ctx.user(),
408 desc=ctx.description(),
408 desc=ctx.description(),
409 date=ctx.date(),
409 date=ctx.date(),
410 rev=i,
410 rev=i,
411 node=hn,
411 node=hn,
412 tags=webutil.nodetagsdict(web.repo, n),
412 tags=webutil.nodetagsdict(web.repo, n),
413 inbranch=webutil.nodeinbranch(web.repo, ctx),
413 inbranch=webutil.nodeinbranch(web.repo, ctx),
414 branches=webutil.nodebranchdict(web.repo, ctx)))
414 branches=webutil.nodebranchdict(web.repo, ctx)))
415
415
416 yield l
416 yield l
417
417
418 cl = web.repo.changelog
418 cl = web.repo.changelog
419 count = len(cl)
419 count = len(cl)
420 start = max(0, count - web.maxchanges)
420 start = max(0, count - web.maxchanges)
421 end = min(count, start + web.maxchanges)
421 end = min(count, start + web.maxchanges)
422
422
423 return tmpl("summary",
423 return tmpl("summary",
424 desc=web.config("web", "description", "unknown"),
424 desc=web.config("web", "description", "unknown"),
425 owner=get_contact(web.config) or "unknown",
425 owner=get_contact(web.config) or "unknown",
426 lastchange=cl.read(cl.tip())[2],
426 lastchange=cl.read(cl.tip())[2],
427 tags=tagentries,
427 tags=tagentries,
428 branches=branches,
428 branches=branches,
429 shortlog=changelist,
429 shortlog=changelist,
430 node=hex(cl.tip()),
430 node=hex(cl.tip()),
431 archives=web.archivelist("tip"))
431 archives=web.archivelist("tip"))
432
432
433 def filediff(web, req, tmpl):
433 def filediff(web, req, tmpl):
434 fctx, ctx = None, None
434 fctx, ctx = None, None
435 try:
435 try:
436 fctx = webutil.filectx(web.repo, req)
436 fctx = webutil.filectx(web.repo, req)
437 except LookupError:
437 except LookupError:
438 ctx = webutil.changectx(web.repo, req)
438 ctx = webutil.changectx(web.repo, req)
439 path = webutil.cleanpath(web.repo, req.form['file'][0])
439 path = webutil.cleanpath(web.repo, req.form['file'][0])
440 if path not in ctx.files():
440 if path not in ctx.files():
441 raise
441 raise
442
442
443 if fctx is not None:
443 if fctx is not None:
444 n = fctx.node()
444 n = fctx.node()
445 path = fctx.path()
445 path = fctx.path()
446 parents = fctx.parents()
446 parents = fctx.parents()
447 p1 = parents and parents[0].node() or nullid
447 p1 = parents and parents[0].node() or nullid
448 else:
448 else:
449 n = ctx.node()
449 n = ctx.node()
450 # path already defined in except clause
450 # path already defined in except clause
451 parents = ctx.parents()
451 parents = ctx.parents()
452
452
453 parity = paritygen(web.stripecount)
453 parity = paritygen(web.stripecount)
454 diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity)
454 diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity)
455 rename = fctx and webutil.renamelink(fctx) or []
455 rename = fctx and webutil.renamelink(fctx) or []
456 ctx = fctx and fctx or ctx
456 ctx = fctx and fctx or ctx
457 return tmpl("filediff",
457 return tmpl("filediff",
458 file=path,
458 file=path,
459 node=hex(n),
459 node=hex(n),
460 rev=ctx.rev(),
460 rev=ctx.rev(),
461 date=ctx.date(),
461 date=ctx.date(),
462 desc=ctx.description(),
462 desc=ctx.description(),
463 author=ctx.user(),
463 author=ctx.user(),
464 rename=rename,
464 rename=rename,
465 branch=webutil.nodebranchnodefault(ctx),
465 branch=webutil.nodebranchnodefault(ctx),
466 parent=webutil.siblings(parents),
466 parent=webutil.siblings(parents),
467 child=webutil.siblings(ctx.children()),
467 child=webutil.siblings(ctx.children()),
468 diff=diffs)
468 diff=diffs)
469
469
470 diff = filediff
470 diff = filediff
471
471
472 def annotate(web, req, tmpl):
472 def annotate(web, req, tmpl):
473 fctx = webutil.filectx(web.repo, req)
473 fctx = webutil.filectx(web.repo, req)
474 f = fctx.path()
474 f = fctx.path()
475 parity = paritygen(web.stripecount)
475 parity = paritygen(web.stripecount)
476
476
477 def annotate(**map):
477 def annotate(**map):
478 last = None
478 last = None
479 if binary(fctx.data()):
479 if binary(fctx.data()):
480 mt = (mimetypes.guess_type(fctx.path())[0]
480 mt = (mimetypes.guess_type(fctx.path())[0]
481 or 'application/octet-stream')
481 or 'application/octet-stream')
482 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
482 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
483 '(binary:%s)' % mt)])
483 '(binary:%s)' % mt)])
484 else:
484 else:
485 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
485 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
486 for lineno, ((f, targetline), l) in lines:
486 for lineno, ((f, targetline), l) in lines:
487 fnode = f.filenode()
487 fnode = f.filenode()
488
488
489 if last != fnode:
489 if last != fnode:
490 last = fnode
490 last = fnode
491
491
492 yield {"parity": parity.next(),
492 yield {"parity": parity.next(),
493 "node": hex(f.node()),
493 "node": hex(f.node()),
494 "rev": f.rev(),
494 "rev": f.rev(),
495 "author": f.user(),
495 "author": f.user(),
496 "desc": f.description(),
496 "desc": f.description(),
497 "file": f.path(),
497 "file": f.path(),
498 "targetline": targetline,
498 "targetline": targetline,
499 "line": l,
499 "line": l,
500 "lineid": "l%d" % (lineno + 1),
500 "lineid": "l%d" % (lineno + 1),
501 "linenumber": "% 6d" % (lineno + 1)}
501 "linenumber": "% 6d" % (lineno + 1)}
502
502
503 return tmpl("fileannotate",
503 return tmpl("fileannotate",
504 file=f,
504 file=f,
505 annotate=annotate,
505 annotate=annotate,
506 path=webutil.up(f),
506 path=webutil.up(f),
507 rev=fctx.rev(),
507 rev=fctx.rev(),
508 node=hex(fctx.node()),
508 node=hex(fctx.node()),
509 author=fctx.user(),
509 author=fctx.user(),
510 date=fctx.date(),
510 date=fctx.date(),
511 desc=fctx.description(),
511 desc=fctx.description(),
512 rename=webutil.renamelink(fctx),
512 rename=webutil.renamelink(fctx),
513 branch=webutil.nodebranchnodefault(fctx),
513 branch=webutil.nodebranchnodefault(fctx),
514 parent=webutil.siblings(fctx.parents()),
514 parent=webutil.siblings(fctx.parents()),
515 child=webutil.siblings(fctx.children()),
515 child=webutil.siblings(fctx.children()),
516 permissions=fctx.manifest().flags(f))
516 permissions=fctx.manifest().flags(f))
517
517
518 def filelog(web, req, tmpl):
518 def filelog(web, req, tmpl):
519
519
520 try:
520 try:
521 fctx = webutil.filectx(web.repo, req)
521 fctx = webutil.filectx(web.repo, req)
522 f = fctx.path()
522 f = fctx.path()
523 fl = fctx.filelog()
523 fl = fctx.filelog()
524 except revlog.LookupError:
524 except revlog.LookupError:
525 f = webutil.cleanpath(web.repo, req.form['file'][0])
525 f = webutil.cleanpath(web.repo, req.form['file'][0])
526 fl = web.repo.file(f)
526 fl = web.repo.file(f)
527 numrevs = len(fl)
527 numrevs = len(fl)
528 if not numrevs: # file doesn't exist at all
528 if not numrevs: # file doesn't exist at all
529 raise
529 raise
530 rev = webutil.changectx(web.repo, req).rev()
530 rev = webutil.changectx(web.repo, req).rev()
531 first = fl.linkrev(0)
531 first = fl.linkrev(0)
532 if rev < first: # current rev is from before file existed
532 if rev < first: # current rev is from before file existed
533 raise
533 raise
534 frev = numrevs - 1
534 frev = numrevs - 1
535 while fl.linkrev(frev) > rev:
535 while fl.linkrev(frev) > rev:
536 frev -= 1
536 frev -= 1
537 fctx = web.repo.filectx(f, fl.linkrev(frev))
537 fctx = web.repo.filectx(f, fl.linkrev(frev))
538
538
539 count = fctx.filerev() + 1
539 count = fctx.filerev() + 1
540 pagelen = web.maxshortchanges
540 pagelen = web.maxshortchanges
541 start = max(0, fctx.filerev() - pagelen + 1) # first rev on this page
541 start = max(0, fctx.filerev() - pagelen + 1) # first rev on this page
542 end = min(count, start + pagelen) # last rev on this page
542 end = min(count, start + pagelen) # last rev on this page
543 parity = paritygen(web.stripecount, offset=start-end)
543 parity = paritygen(web.stripecount, offset=start-end)
544
544
545 def entries(limit=0, **map):
545 def entries(limit=0, **map):
546 l = []
546 l = []
547
547
548 for i in xrange(start, end):
548 for i in xrange(start, end):
549 ctx = fctx.filectx(i)
549 ctx = fctx.filectx(i)
550
550
551 l.insert(0, {"parity": parity.next(),
551 l.insert(0, {"parity": parity.next(),
552 "filerev": i,
552 "filerev": i,
553 "file": f,
553 "file": f,
554 "node": hex(ctx.node()),
554 "node": hex(ctx.node()),
555 "author": ctx.user(),
555 "author": ctx.user(),
556 "date": ctx.date(),
556 "date": ctx.date(),
557 "rename": webutil.renamelink(fctx),
557 "rename": webutil.renamelink(fctx),
558 "parent": webutil.siblings(fctx.parents()),
558 "parent": webutil.siblings(fctx.parents()),
559 "child": webutil.siblings(fctx.children()),
559 "child": webutil.siblings(fctx.children()),
560 "desc": ctx.description(),
560 "desc": ctx.description(),
561 "tags": webutil.nodetagsdict(web.repo, ctx.node()),
561 "tags": webutil.nodetagsdict(web.repo, ctx.node()),
562 "branch": webutil.nodebranchnodefault(ctx),
562 "branch": webutil.nodebranchnodefault(ctx),
563 "inbranch": webutil.nodeinbranch(web.repo, ctx),
563 "inbranch": webutil.nodeinbranch(web.repo, ctx),
564 "branches": webutil.nodebranchdict(web.repo, ctx)})
564 "branches": webutil.nodebranchdict(web.repo, ctx)})
565
565
566 if limit > 0:
566 if limit > 0:
567 l = l[:limit]
567 l = l[:limit]
568
568
569 for e in l:
569 for e in l:
570 yield e
570 yield e
571
571
572 nodefunc = lambda x: fctx.filectx(fileid=x)
572 nodefunc = lambda x: fctx.filectx(fileid=x)
573 nav = webutil.revnavgen(end - 1, pagelen, count, nodefunc)
573 nav = webutil.revnavgen(end - 1, pagelen, count, nodefunc)
574 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
574 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
575 entries=lambda **x: entries(limit=0, **x),
575 entries=lambda **x: entries(limit=0, **x),
576 latestentry=lambda **x: entries(limit=1, **x))
576 latestentry=lambda **x: entries(limit=1, **x))
577
577
578
578
579 def archive(web, req, tmpl):
579 def archive(web, req, tmpl):
580 type_ = req.form.get('type', [None])[0]
580 type_ = req.form.get('type', [None])[0]
581 allowed = web.configlist("web", "allow_archive")
581 allowed = web.configlist("web", "allow_archive")
582 key = req.form['node'][0]
582 key = req.form['node'][0]
583
583
584 if type_ not in web.archives:
584 if type_ not in web.archives:
585 msg = 'Unsupported archive type: %s' % type_
585 msg = 'Unsupported archive type: %s' % type_
586 raise ErrorResponse(HTTP_NOT_FOUND, msg)
586 raise ErrorResponse(HTTP_NOT_FOUND, msg)
587
587
588 if not ((type_ in allowed or
588 if not ((type_ in allowed or
589 web.configbool("web", "allow" + type_, False))):
589 web.configbool("web", "allow" + type_, False))):
590 msg = 'Archive type not allowed: %s' % type_
590 msg = 'Archive type not allowed: %s' % type_
591 raise ErrorResponse(HTTP_FORBIDDEN, msg)
591 raise ErrorResponse(HTTP_FORBIDDEN, msg)
592
592
593 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
593 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
594 cnode = web.repo.lookup(key)
594 cnode = web.repo.lookup(key)
595 arch_version = key
595 arch_version = key
596 if cnode == key or key == 'tip':
596 if cnode == key or key == 'tip':
597 arch_version = short(cnode)
597 arch_version = short(cnode)
598 name = "%s-%s" % (reponame, arch_version)
598 name = "%s-%s" % (reponame, arch_version)
599 mimetype, artype, extension, encoding = web.archive_specs[type_]
599 mimetype, artype, extension, encoding = web.archive_specs[type_]
600 headers = [
600 headers = [
601 ('Content-Type', mimetype),
601 ('Content-Type', mimetype),
602 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
602 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
603 ]
603 ]
604 if encoding:
604 if encoding:
605 headers.append(('Content-Encoding', encoding))
605 headers.append(('Content-Encoding', encoding))
606 req.header(headers)
606 req.header(headers)
607 req.respond(HTTP_OK)
607 req.respond(HTTP_OK)
608 archival.archive(web.repo, req, cnode, artype, prefix=name)
608 archival.archive(web.repo, req, cnode, artype, prefix=name)
609 return []
609 return []
610
610
611
611
612 def static(web, req, tmpl):
612 def static(web, req, tmpl):
613 fname = req.form['file'][0]
613 fname = req.form['file'][0]
614 # a repo owner may set web.static in .hg/hgrc to get any file
614 # a repo owner may set web.static in .hg/hgrc to get any file
615 # readable by the user running the CGI script
615 # readable by the user running the CGI script
616 static = web.config("web", "static", None, untrusted=False)
616 static = web.config("web", "static", None, untrusted=False)
617 if not static:
617 if not static:
618 tp = web.templatepath
618 tp = web.templatepath
619 if isinstance(tp, str):
619 if isinstance(tp, str):
620 tp = [tp]
620 tp = [tp]
621 static = [os.path.join(p, 'static') for p in tp]
621 static = [os.path.join(p, 'static') for p in tp]
622 return [staticfile(static, fname, req)]
622 return [staticfile(static, fname, req)]
623
623
624 def graph(web, req, tmpl):
624 def graph(web, req, tmpl):
625 rev = webutil.changectx(web.repo, req).rev()
625 rev = webutil.changectx(web.repo, req).rev()
626 bg_height = 39
626 bg_height = 39
627
627
628 revcount = 25
628 revcount = 25
629 if 'revcount' in req.form:
629 if 'revcount' in req.form:
630 revcount = int(req.form.get('revcount', [revcount])[0])
630 revcount = int(req.form.get('revcount', [revcount])[0])
631 tmpl.defaults['sessionvars']['revcount'] = revcount
631 tmpl.defaults['sessionvars']['revcount'] = revcount
632
632
633 lessvars = copy.copy(tmpl.defaults['sessionvars'])
633 lessvars = copy.copy(tmpl.defaults['sessionvars'])
634 lessvars['revcount'] = revcount / 2
634 lessvars['revcount'] = revcount / 2
635 morevars = copy.copy(tmpl.defaults['sessionvars'])
635 morevars = copy.copy(tmpl.defaults['sessionvars'])
636 morevars['revcount'] = revcount * 2
636 morevars['revcount'] = revcount * 2
637
637
638 max_rev = len(web.repo) - 1
638 max_rev = len(web.repo) - 1
639 revcount = min(max_rev, revcount)
639 revcount = min(max_rev, revcount)
640 revnode = web.repo.changelog.node(rev)
640 revnode = web.repo.changelog.node(rev)
641 revnode_hex = hex(revnode)
641 revnode_hex = hex(revnode)
642 uprev = min(max_rev, rev + revcount)
642 uprev = min(max_rev, rev + revcount)
643 downrev = max(0, rev - revcount)
643 downrev = max(0, rev - revcount)
644 count = len(web.repo)
644 count = len(web.repo)
645 changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
645 changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
646
646
647 tree = list(graphmod.graph(web.repo, rev, downrev))
647 tree = list(graphmod.graph(web.repo, rev, downrev))
648 canvasheight = (len(tree) + 1) * bg_height - 27;
648 canvasheight = (len(tree) + 1) * bg_height - 27;
649 data = []
649 data = []
650 for i, (ctx, vtx, edges) in enumerate(tree):
650 for i, (ctx, vtx, edges) in enumerate(tree):
651 node = short(ctx.node())
651 node = short(ctx.node())
652 age = templatefilters.age(ctx.date())
652 age = templatefilters.age(ctx.date())
653 desc = templatefilters.firstline(ctx.description())
653 desc = templatefilters.firstline(ctx.description())
654 desc = cgi.escape(desc)
654 desc = cgi.escape(desc)
655 user = cgi.escape(templatefilters.person(ctx.user()))
655 user = cgi.escape(templatefilters.person(ctx.user()))
656 branch = ctx.branch()
656 branch = ctx.branch()
657 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
657 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
658 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
658 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
659
659
660 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
660 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
661 lessvars=lessvars, morevars=morevars, downrev=downrev,
661 lessvars=lessvars, morevars=morevars, downrev=downrev,
662 canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
662 canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
663 node=revnode_hex, changenav=changenav)
663 node=revnode_hex, changenav=changenav)
General Comments 0
You need to be logged in to leave comments. Login now