##// END OF EJS Templates
hgweb: working diff for removed files
Dirkjan Ochtman -
r7183:099b4f9b default
parent child Browse files
Show More
@@ -0,0 +1,17 b''
1 echo % setting up repo
2 hg init test
3 cd test
4 echo a > a
5 hg ci -Ama
6 hg rm a
7 hg ci -mdel
8
9 echo % set up hgweb
10 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
11 cat hg.pid >> $DAEMON_PIDS
12
13 echo % revision
14 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/tip'
15
16 echo % diff removed file
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a'
@@ -0,0 +1,130 b''
1 % setting up repo
2 adding a
3 % set up hgweb
4 % revision
5 200 Script output follows
6
7 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
8 <html>
9 <head>
10 <link rel="icon" href="/static/hgicon.png" type="image/png">
11 <meta name="robots" content="index, nofollow" />
12 <link rel="stylesheet" href="/static/style.css" type="text/css" />
13
14 <title>test: changeset c78f6c5cbea9</title>
15 </head>
16 <body>
17
18 <div class="buttons">
19 <a href="/log/1">changelog</a>
20 <a href="/shortlog/1">shortlog</a>
21 <a href="/graph">graph</a>
22 <a href="/tags">tags</a>
23 <a href="/file/c78f6c5cbea9">files</a>
24 <a href="/raw-rev/c78f6c5cbea9">raw</a>
25
26 </div>
27
28 <h2>changeset: del</h2>
29
30 <table id="changesetEntry">
31 <tr>
32 <th class="changeset">changeset 1:</th>
33 <td class="changeset"><a href="/rev/c78f6c5cbea9">c78f6c5cbea9</a></td>
34 </tr>
35 <tr><th class="parent">parent 0:</th><td class="parent"><a href="/rev/cb9a9f314b8b">cb9a9f314b8b</a></td></tr>
36
37 <tr><th class="tag">tag:</th><td class="tag">tip</td></tr>
38 <tr>
39 <th class="author">author:</th>
40 <td class="author">&#116;&#101;&#115;&#116;</td>
41 </tr>
42 <tr>
43 <th class="date">date:</th>
44 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (38 years ago)</td>
45 </tr>
46 <tr>
47 <th class="files">files:</th>
48 <td class="files">a </td>
49 </tr>
50 <tr>
51 <th class="description">description:</th>
52 <td class="description">del</td>
53 </tr>
54 </table>
55
56 <div id="changesetDiff">
57 <pre class="parity0"><span class="minusline"><a class="lineno" href="#l1" id="l1"> 1</a>--- a/a Thu Jan 01 00:00:00 1970 +0000
58 </span><span class="plusline"><a class="lineno" href="#l2" id="l2"> 2</a>+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
59 </span><span class="atline"><a class="lineno" href="#l3" id="l3"> 3</a>@@ -1,1 +0,0 @@
60 </span><span class="minusline"><a class="lineno" href="#l4" id="l4"> 4</a>-a
61 </span></pre>
62 </div>
63
64
65 <div class="logo">
66 <a href="http://www.selenic.com/mercurial/">
67 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
68 </div>
69
70 </body>
71 </html>
72
73
74
75 % diff removed file
76 200 Script output follows
77
78 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
79 <html>
80 <head>
81 <link rel="icon" href="/static/hgicon.png" type="image/png">
82 <meta name="robots" content="index, nofollow" />
83 <link rel="stylesheet" href="/static/style.css" type="text/css" />
84
85 <title>test: a diff</title>
86 </head>
87 <body>
88
89 <div class="buttons">
90 <a href="/log/1">changelog</a>
91 <a href="/shortlog/1">shortlog</a>
92 <a href="/graph">graph</a>
93 <a href="/tags">tags</a>
94 <a href="/rev/c78f6c5cbea9">changeset</a>
95 <a href="/file/c78f6c5cbea9/a">file</a>
96 <a href="/log/c78f6c5cbea9/a">revisions</a>
97 <a href="/annotate/c78f6c5cbea9/a">annotate</a>
98 <a href="/raw-diff/c78f6c5cbea9/a">raw</a>
99 </div>
100
101 <h2>a</h2>
102
103 <table id="filediffEntry">
104 <tr>
105 <th class="revision">revision 1:</th>
106 <td class="revision"><a href="/rev/c78f6c5cbea9">c78f6c5cbea9</a></td>
107 </tr>
108 <tr><th class="parent">parent 0:</th><td class="parent"><a href="/rev/cb9a9f314b8b">cb9a9f314b8b</a></td></tr>
109
110 </table>
111
112 <div id="fileDiff">
113 <pre class="parity0"><span class="minusline"><a class="lineno" href="#l1" id="l1"> 1</a>--- a/a Thu Jan 01 00:00:00 1970 +0000
114 </span><span class="plusline"><a class="lineno" href="#l2" id="l2"> 2</a>+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
115 </span><span class="atline"><a class="lineno" href="#l3" id="l3"> 3</a>@@ -1,1 +0,0 @@
116 </span><span class="minusline"><a class="lineno" href="#l4" id="l4"> 4</a>-a
117 </span></pre>
118 </div>
119
120
121 <div class="logo">
122 <a href="http://www.selenic.com/mercurial/">
123 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
124 </div>
125
126 </body>
127 </html>
128
129
130
@@ -1,615 +1,631 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
8 import os, mimetypes, re, cgi
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 fl = fctx.filelog()
62 fl = fctx.filelog()
63 n = fctx.filenode()
63 n = fctx.filenode()
64 parity = paritygen(web.stripecount)
64 parity = paritygen(web.stripecount)
65
65
66 if binary(text):
66 if binary(text):
67 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
67 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
68 text = '(binary:%s)' % mt
68 text = '(binary:%s)' % mt
69
69
70 def lines():
70 def lines():
71 for lineno, t in enumerate(text.splitlines(1)):
71 for lineno, t in enumerate(text.splitlines(1)):
72 yield {"line": t,
72 yield {"line": t,
73 "lineid": "l%d" % (lineno + 1),
73 "lineid": "l%d" % (lineno + 1),
74 "linenumber": "% 6d" % (lineno + 1),
74 "linenumber": "% 6d" % (lineno + 1),
75 "parity": parity.next()}
75 "parity": parity.next()}
76
76
77 return tmpl("filerevision",
77 return tmpl("filerevision",
78 file=f,
78 file=f,
79 path=webutil.up(f),
79 path=webutil.up(f),
80 text=lines(),
80 text=lines(),
81 rev=fctx.rev(),
81 rev=fctx.rev(),
82 node=hex(fctx.node()),
82 node=hex(fctx.node()),
83 author=fctx.user(),
83 author=fctx.user(),
84 date=fctx.date(),
84 date=fctx.date(),
85 desc=fctx.description(),
85 desc=fctx.description(),
86 branch=webutil.nodebranchnodefault(fctx),
86 branch=webutil.nodebranchnodefault(fctx),
87 parent=webutil.siblings(fctx.parents()),
87 parent=webutil.siblings(fctx.parents()),
88 child=webutil.siblings(fctx.children()),
88 child=webutil.siblings(fctx.children()),
89 rename=webutil.renamelink(fctx),
89 rename=webutil.renamelink(fctx),
90 permissions=fctx.manifest().flags(f))
90 permissions=fctx.manifest().flags(f))
91
91
92 def file(web, req, tmpl):
92 def file(web, req, tmpl):
93 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
93 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
94 if not path:
94 if not path:
95 return manifest(web, req, tmpl)
95 return manifest(web, req, tmpl)
96 try:
96 try:
97 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
97 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
98 except revlog.LookupError, inst:
98 except revlog.LookupError, inst:
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(len(cl) - 1, 0, -100):
112 for i in xrange(len(cl) - 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[j]
115 ctx = web.repo[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 = len(web.repo) - 1
170 hi = len(web.repo) - 1
171 try:
171 try:
172 ctx = web.repo[hi]
172 ctx = web.repo[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[i]
180 ctx = web.repo[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 = 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 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 template = f in ctx and 'filenodelink' or 'filenolink'
237 template = f in ctx and 'filenodelink' or 'filenolink'
238 files.append(tmpl(template,
238 files.append(tmpl(template,
239 node=hex(n), file=f,
239 node=hex(n), file=f,
240 parity=parity.next()))
240 parity=parity.next()))
241
241
242 diffs = web.diff(tmpl, p1, n, None)
242 diffs = web.diff(tmpl, p1, n, None)
243 return tmpl('changeset',
243 return tmpl('changeset',
244 diff=diffs,
244 diff=diffs,
245 rev=ctx.rev(),
245 rev=ctx.rev(),
246 node=hex(n),
246 node=hex(n),
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 author=ctx.user(),
250 author=ctx.user(),
251 desc=ctx.description(),
251 desc=ctx.description(),
252 date=ctx.date(),
252 date=ctx.date(),
253 files=files,
253 files=files,
254 archives=web.archivelist(hex(n)),
254 archives=web.archivelist(hex(n)),
255 tags=webutil.nodetagsdict(web.repo, n),
255 tags=webutil.nodetagsdict(web.repo, n),
256 branch=webutil.nodebranchnodefault(ctx),
256 branch=webutil.nodebranchnodefault(ctx),
257 inbranch=webutil.nodeinbranch(web.repo, ctx),
257 inbranch=webutil.nodeinbranch(web.repo, ctx),
258 branches=webutil.nodebranchdict(web.repo, ctx))
258 branches=webutil.nodebranchdict(web.repo, ctx))
259
259
260 rev = changeset
260 rev = changeset
261
261
262 def manifest(web, req, tmpl):
262 def manifest(web, req, tmpl):
263 ctx = webutil.changectx(web.repo, req)
263 ctx = webutil.changectx(web.repo, req)
264 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
264 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
265 mf = ctx.manifest()
265 mf = ctx.manifest()
266 node = ctx.node()
266 node = ctx.node()
267
267
268 files = {}
268 files = {}
269 parity = paritygen(web.stripecount)
269 parity = paritygen(web.stripecount)
270
270
271 if path and path[-1] != "/":
271 if path and path[-1] != "/":
272 path += "/"
272 path += "/"
273 l = len(path)
273 l = len(path)
274 abspath = "/" + path
274 abspath = "/" + path
275
275
276 for f, n in mf.items():
276 for f, n in mf.items():
277 if f[:l] != path:
277 if f[:l] != path:
278 continue
278 continue
279 remain = f[l:]
279 remain = f[l:]
280 idx = remain.find('/')
280 idx = remain.find('/')
281 if idx != -1:
281 if idx != -1:
282 remain = remain[:idx+1]
282 remain = remain[:idx+1]
283 n = None
283 n = None
284 files[remain] = (f, n)
284 files[remain] = (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 for f in util.sort(files):
290 for f in util.sort(files):
291 full, fnode = files[f]
291 full, fnode = files[f]
292 if not fnode:
292 if not fnode:
293 continue
293 continue
294
294
295 fctx = ctx.filectx(full)
295 fctx = ctx.filectx(full)
296 yield {"file": full,
296 yield {"file": full,
297 "parity": parity.next(),
297 "parity": parity.next(),
298 "basename": f,
298 "basename": f,
299 "date": fctx.date(),
299 "date": fctx.date(),
300 "size": fctx.size(),
300 "size": fctx.size(),
301 "permissions": mf.flags(full)}
301 "permissions": mf.flags(full)}
302
302
303 def dirlist(**map):
303 def dirlist(**map):
304 for f in util.sort(files):
304 for f in util.sort(files):
305 full, fnode = files[f]
305 full, fnode = files[f]
306 if fnode:
306 if fnode:
307 continue
307 continue
308
308
309 yield {"parity": parity.next(),
309 yield {"parity": parity.next(),
310 "path": "%s%s" % (abspath, f),
310 "path": "%s%s" % (abspath, f),
311 "basename": f[:-1]}
311 "basename": f[:-1]}
312
312
313 return tmpl("manifest",
313 return tmpl("manifest",
314 rev=ctx.rev(),
314 rev=ctx.rev(),
315 node=hex(node),
315 node=hex(node),
316 path=abspath,
316 path=abspath,
317 up=webutil.up(abspath),
317 up=webutil.up(abspath),
318 upparity=parity.next(),
318 upparity=parity.next(),
319 fentries=filelist,
319 fentries=filelist,
320 dentries=dirlist,
320 dentries=dirlist,
321 archives=web.archivelist(hex(node)),
321 archives=web.archivelist(hex(node)),
322 tags=webutil.nodetagsdict(web.repo, node),
322 tags=webutil.nodetagsdict(web.repo, node),
323 inbranch=webutil.nodeinbranch(web.repo, ctx),
323 inbranch=webutil.nodeinbranch(web.repo, ctx),
324 branches=webutil.nodebranchdict(web.repo, ctx))
324 branches=webutil.nodebranchdict(web.repo, ctx))
325
325
326 def tags(web, req, tmpl):
326 def tags(web, req, tmpl):
327 i = web.repo.tagslist()
327 i = web.repo.tagslist()
328 i.reverse()
328 i.reverse()
329 parity = paritygen(web.stripecount)
329 parity = paritygen(web.stripecount)
330
330
331 def entries(notip=False,limit=0, **map):
331 def entries(notip=False,limit=0, **map):
332 count = 0
332 count = 0
333 for k, n in i:
333 for k, n in i:
334 if notip and k == "tip":
334 if notip and k == "tip":
335 continue
335 continue
336 if limit > 0 and count >= limit:
336 if limit > 0 and count >= limit:
337 continue
337 continue
338 count = count + 1
338 count = count + 1
339 yield {"parity": parity.next(),
339 yield {"parity": parity.next(),
340 "tag": k,
340 "tag": k,
341 "date": web.repo[n].date(),
341 "date": web.repo[n].date(),
342 "node": hex(n)}
342 "node": hex(n)}
343
343
344 return tmpl("tags",
344 return tmpl("tags",
345 node=hex(web.repo.changelog.tip()),
345 node=hex(web.repo.changelog.tip()),
346 entries=lambda **x: entries(False,0, **x),
346 entries=lambda **x: entries(False,0, **x),
347 entriesnotip=lambda **x: entries(True,0, **x),
347 entriesnotip=lambda **x: entries(True,0, **x),
348 latestentry=lambda **x: entries(True,1, **x))
348 latestentry=lambda **x: entries(True,1, **x))
349
349
350 def summary(web, req, tmpl):
350 def summary(web, req, tmpl):
351 i = web.repo.tagslist()
351 i = web.repo.tagslist()
352 i.reverse()
352 i.reverse()
353
353
354 def tagentries(**map):
354 def tagentries(**map):
355 parity = paritygen(web.stripecount)
355 parity = paritygen(web.stripecount)
356 count = 0
356 count = 0
357 for k, n in i:
357 for k, n in i:
358 if k == "tip": # skip tip
358 if k == "tip": # skip tip
359 continue
359 continue
360
360
361 count += 1
361 count += 1
362 if count > 10: # limit to 10 tags
362 if count > 10: # limit to 10 tags
363 break
363 break
364
364
365 yield tmpl("tagentry",
365 yield tmpl("tagentry",
366 parity=parity.next(),
366 parity=parity.next(),
367 tag=k,
367 tag=k,
368 node=hex(n),
368 node=hex(n),
369 date=web.repo[n].date())
369 date=web.repo[n].date())
370
370
371 def branches(**map):
371 def branches(**map):
372 parity = paritygen(web.stripecount)
372 parity = paritygen(web.stripecount)
373
373
374 b = web.repo.branchtags()
374 b = web.repo.branchtags()
375 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
375 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
376 for r,n,t in util.sort(l):
376 for r,n,t in util.sort(l):
377 yield {'parity': parity.next(),
377 yield {'parity': parity.next(),
378 'branch': t,
378 'branch': t,
379 'node': hex(n),
379 'node': hex(n),
380 'date': web.repo[n].date()}
380 'date': web.repo[n].date()}
381
381
382 def changelist(**map):
382 def changelist(**map):
383 parity = paritygen(web.stripecount, offset=start-end)
383 parity = paritygen(web.stripecount, offset=start-end)
384 l = [] # build a list in forward order for efficiency
384 l = [] # build a list in forward order for efficiency
385 for i in xrange(start, end):
385 for i in xrange(start, end):
386 ctx = web.repo[i]
386 ctx = web.repo[i]
387 n = ctx.node()
387 n = ctx.node()
388 hn = hex(n)
388 hn = hex(n)
389
389
390 l.insert(0, tmpl(
390 l.insert(0, tmpl(
391 'shortlogentry',
391 'shortlogentry',
392 parity=parity.next(),
392 parity=parity.next(),
393 author=ctx.user(),
393 author=ctx.user(),
394 desc=ctx.description(),
394 desc=ctx.description(),
395 date=ctx.date(),
395 date=ctx.date(),
396 rev=i,
396 rev=i,
397 node=hn,
397 node=hn,
398 tags=webutil.nodetagsdict(web.repo, n),
398 tags=webutil.nodetagsdict(web.repo, n),
399 inbranch=webutil.nodeinbranch(web.repo, ctx),
399 inbranch=webutil.nodeinbranch(web.repo, ctx),
400 branches=webutil.nodebranchdict(web.repo, ctx)))
400 branches=webutil.nodebranchdict(web.repo, ctx)))
401
401
402 yield l
402 yield l
403
403
404 cl = web.repo.changelog
404 cl = web.repo.changelog
405 count = len(cl)
405 count = len(cl)
406 start = max(0, count - web.maxchanges)
406 start = max(0, count - web.maxchanges)
407 end = min(count, start + web.maxchanges)
407 end = min(count, start + web.maxchanges)
408
408
409 return tmpl("summary",
409 return tmpl("summary",
410 desc=web.config("web", "description", "unknown"),
410 desc=web.config("web", "description", "unknown"),
411 owner=get_contact(web.config) or "unknown",
411 owner=get_contact(web.config) or "unknown",
412 lastchange=cl.read(cl.tip())[2],
412 lastchange=cl.read(cl.tip())[2],
413 tags=tagentries,
413 tags=tagentries,
414 branches=branches,
414 branches=branches,
415 shortlog=changelist,
415 shortlog=changelist,
416 node=hex(cl.tip()),
416 node=hex(cl.tip()),
417 archives=web.archivelist("tip"))
417 archives=web.archivelist("tip"))
418
418
419 def filediff(web, req, tmpl):
419 def filediff(web, req, tmpl):
420 fctx = webutil.filectx(web.repo, req)
420 fctx, ctx = None, None
421 n = fctx.node()
421 try:
422 path = fctx.path()
422 fctx = webutil.filectx(web.repo, req)
423 parents = fctx.parents()
423 except LookupError, inst:
424 p1 = parents and parents[0].node() or nullid
424 ctx = webutil.changectx(web.repo, req)
425 path = webutil.cleanpath(web.repo, req.form['file'][0])
426 if path not in ctx.files():
427 raise
428
429 if fctx is not None:
430 n = fctx.node()
431 path = fctx.path()
432 parents = fctx.parents()
433 p1 = parents and parents[0].node() or nullid
434 else:
435 n = ctx.node()
436 # path already defined in except clause
437 parents = ctx.parents()
438 p1 = parents and parents[0].node() or nullid
425
439
426 diffs = web.diff(tmpl, p1, n, [path])
440 diffs = web.diff(tmpl, p1, n, [path])
441 rename = fctx and webutil.renamelink(fctx) or []
442 ctx = fctx and fctx or ctx
427 return tmpl("filediff",
443 return tmpl("filediff",
428 file=path,
444 file=path,
429 node=hex(n),
445 node=hex(n),
430 rev=fctx.rev(),
446 rev=ctx.rev(),
431 date=fctx.date(),
447 date=ctx.date(),
432 desc=fctx.description(),
448 desc=ctx.description(),
433 author=fctx.user(),
449 author=ctx.user(),
434 rename=webutil.renamelink(fctx),
450 rename=rename,
435 branch=webutil.nodebranchnodefault(fctx),
451 branch=webutil.nodebranchnodefault(ctx),
436 parent=webutil.siblings(parents),
452 parent=webutil.siblings(parents),
437 child=webutil.siblings(fctx.children()),
453 child=webutil.siblings(ctx.children()),
438 diff=diffs)
454 diff=diffs)
439
455
440 diff = filediff
456 diff = filediff
441
457
442 def annotate(web, req, tmpl):
458 def annotate(web, req, tmpl):
443 fctx = webutil.filectx(web.repo, req)
459 fctx = webutil.filectx(web.repo, req)
444 f = fctx.path()
460 f = fctx.path()
445 n = fctx.filenode()
461 n = fctx.filenode()
446 fl = fctx.filelog()
462 fl = fctx.filelog()
447 parity = paritygen(web.stripecount)
463 parity = paritygen(web.stripecount)
448
464
449 def annotate(**map):
465 def annotate(**map):
450 last = None
466 last = None
451 if binary(fctx.data()):
467 if binary(fctx.data()):
452 mt = (mimetypes.guess_type(fctx.path())[0]
468 mt = (mimetypes.guess_type(fctx.path())[0]
453 or 'application/octet-stream')
469 or 'application/octet-stream')
454 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
470 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
455 '(binary:%s)' % mt)])
471 '(binary:%s)' % mt)])
456 else:
472 else:
457 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
473 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
458 for lineno, ((f, targetline), l) in lines:
474 for lineno, ((f, targetline), l) in lines:
459 fnode = f.filenode()
475 fnode = f.filenode()
460
476
461 if last != fnode:
477 if last != fnode:
462 last = fnode
478 last = fnode
463
479
464 yield {"parity": parity.next(),
480 yield {"parity": parity.next(),
465 "node": hex(f.node()),
481 "node": hex(f.node()),
466 "rev": f.rev(),
482 "rev": f.rev(),
467 "author": f.user(),
483 "author": f.user(),
468 "desc": f.description(),
484 "desc": f.description(),
469 "file": f.path(),
485 "file": f.path(),
470 "targetline": targetline,
486 "targetline": targetline,
471 "line": l,
487 "line": l,
472 "lineid": "l%d" % (lineno + 1),
488 "lineid": "l%d" % (lineno + 1),
473 "linenumber": "% 6d" % (lineno + 1)}
489 "linenumber": "% 6d" % (lineno + 1)}
474
490
475 return tmpl("fileannotate",
491 return tmpl("fileannotate",
476 file=f,
492 file=f,
477 annotate=annotate,
493 annotate=annotate,
478 path=webutil.up(f),
494 path=webutil.up(f),
479 rev=fctx.rev(),
495 rev=fctx.rev(),
480 node=hex(fctx.node()),
496 node=hex(fctx.node()),
481 author=fctx.user(),
497 author=fctx.user(),
482 date=fctx.date(),
498 date=fctx.date(),
483 desc=fctx.description(),
499 desc=fctx.description(),
484 rename=webutil.renamelink(fctx),
500 rename=webutil.renamelink(fctx),
485 branch=webutil.nodebranchnodefault(fctx),
501 branch=webutil.nodebranchnodefault(fctx),
486 parent=webutil.siblings(fctx.parents()),
502 parent=webutil.siblings(fctx.parents()),
487 child=webutil.siblings(fctx.children()),
503 child=webutil.siblings(fctx.children()),
488 permissions=fctx.manifest().flags(f))
504 permissions=fctx.manifest().flags(f))
489
505
490 def filelog(web, req, tmpl):
506 def filelog(web, req, tmpl):
491 fctx = webutil.filectx(web.repo, req)
507 fctx = webutil.filectx(web.repo, req)
492 f = fctx.path()
508 f = fctx.path()
493 fl = fctx.filelog()
509 fl = fctx.filelog()
494 count = len(fl)
510 count = len(fl)
495 pagelen = web.maxshortchanges
511 pagelen = web.maxshortchanges
496 pos = fctx.filerev()
512 pos = fctx.filerev()
497 start = max(0, pos - pagelen + 1)
513 start = max(0, pos - pagelen + 1)
498 end = min(count, start + pagelen)
514 end = min(count, start + pagelen)
499 pos = end - 1
515 pos = end - 1
500 parity = paritygen(web.stripecount, offset=start-end)
516 parity = paritygen(web.stripecount, offset=start-end)
501
517
502 def entries(limit=0, **map):
518 def entries(limit=0, **map):
503 l = []
519 l = []
504
520
505 for i in xrange(start, end):
521 for i in xrange(start, end):
506 ctx = fctx.filectx(i)
522 ctx = fctx.filectx(i)
507 n = fl.node(i)
523 n = fl.node(i)
508
524
509 l.insert(0, {"parity": parity.next(),
525 l.insert(0, {"parity": parity.next(),
510 "filerev": i,
526 "filerev": i,
511 "file": f,
527 "file": f,
512 "node": hex(ctx.node()),
528 "node": hex(ctx.node()),
513 "author": ctx.user(),
529 "author": ctx.user(),
514 "date": ctx.date(),
530 "date": ctx.date(),
515 "rename": webutil.renamelink(fctx),
531 "rename": webutil.renamelink(fctx),
516 "parent": webutil.siblings(fctx.parents()),
532 "parent": webutil.siblings(fctx.parents()),
517 "child": webutil.siblings(fctx.children()),
533 "child": webutil.siblings(fctx.children()),
518 "desc": ctx.description()})
534 "desc": ctx.description()})
519
535
520 if limit > 0:
536 if limit > 0:
521 l = l[:limit]
537 l = l[:limit]
522
538
523 for e in l:
539 for e in l:
524 yield e
540 yield e
525
541
526 nodefunc = lambda x: fctx.filectx(fileid=x)
542 nodefunc = lambda x: fctx.filectx(fileid=x)
527 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
543 nav = webutil.revnavgen(pos, pagelen, count, nodefunc)
528 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
544 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
529 entries=lambda **x: entries(limit=0, **x),
545 entries=lambda **x: entries(limit=0, **x),
530 latestentry=lambda **x: entries(limit=1, **x))
546 latestentry=lambda **x: entries(limit=1, **x))
531
547
532
548
533 def archive(web, req, tmpl):
549 def archive(web, req, tmpl):
534 type_ = req.form.get('type', [None])[0]
550 type_ = req.form.get('type', [None])[0]
535 allowed = web.configlist("web", "allow_archive")
551 allowed = web.configlist("web", "allow_archive")
536 key = req.form['node'][0]
552 key = req.form['node'][0]
537
553
538 if type_ not in web.archives:
554 if type_ not in web.archives:
539 msg = 'Unsupported archive type: %s' % type_
555 msg = 'Unsupported archive type: %s' % type_
540 raise ErrorResponse(HTTP_NOT_FOUND, msg)
556 raise ErrorResponse(HTTP_NOT_FOUND, msg)
541
557
542 if not ((type_ in allowed or
558 if not ((type_ in allowed or
543 web.configbool("web", "allow" + type_, False))):
559 web.configbool("web", "allow" + type_, False))):
544 msg = 'Archive type not allowed: %s' % type_
560 msg = 'Archive type not allowed: %s' % type_
545 raise ErrorResponse(HTTP_FORBIDDEN, msg)
561 raise ErrorResponse(HTTP_FORBIDDEN, msg)
546
562
547 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
563 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
548 cnode = web.repo.lookup(key)
564 cnode = web.repo.lookup(key)
549 arch_version = key
565 arch_version = key
550 if cnode == key or key == 'tip':
566 if cnode == key or key == 'tip':
551 arch_version = short(cnode)
567 arch_version = short(cnode)
552 name = "%s-%s" % (reponame, arch_version)
568 name = "%s-%s" % (reponame, arch_version)
553 mimetype, artype, extension, encoding = web.archive_specs[type_]
569 mimetype, artype, extension, encoding = web.archive_specs[type_]
554 headers = [
570 headers = [
555 ('Content-Type', mimetype),
571 ('Content-Type', mimetype),
556 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
572 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
557 ]
573 ]
558 if encoding:
574 if encoding:
559 headers.append(('Content-Encoding', encoding))
575 headers.append(('Content-Encoding', encoding))
560 req.header(headers)
576 req.header(headers)
561 req.respond(HTTP_OK)
577 req.respond(HTTP_OK)
562 archival.archive(web.repo, req, cnode, artype, prefix=name)
578 archival.archive(web.repo, req, cnode, artype, prefix=name)
563 return []
579 return []
564
580
565
581
566 def static(web, req, tmpl):
582 def static(web, req, tmpl):
567 fname = req.form['file'][0]
583 fname = req.form['file'][0]
568 # a repo owner may set web.static in .hg/hgrc to get any file
584 # a repo owner may set web.static in .hg/hgrc to get any file
569 # readable by the user running the CGI script
585 # readable by the user running the CGI script
570 static = web.config("web", "static", None, untrusted=False)
586 static = web.config("web", "static", None, untrusted=False)
571 if not static:
587 if not static:
572 tp = web.templatepath
588 tp = web.templatepath
573 if isinstance(tp, str):
589 if isinstance(tp, str):
574 tp = [tp]
590 tp = [tp]
575 for path in tp:
591 for path in tp:
576 static = os.path.join(path, 'static')
592 static = os.path.join(path, 'static')
577 if os.path.isdir(static):
593 if os.path.isdir(static):
578 break
594 break
579 return [staticfile(static, fname, req)]
595 return [staticfile(static, fname, req)]
580
596
581 def graph(web, req, tmpl):
597 def graph(web, req, tmpl):
582 rev = webutil.changectx(web.repo, req).rev()
598 rev = webutil.changectx(web.repo, req).rev()
583 bg_height = 39
599 bg_height = 39
584
600
585 max_rev = len(web.repo) - 1
601 max_rev = len(web.repo) - 1
586 revcount = min(max_rev, int(req.form.get('revcount', [25])[0]))
602 revcount = min(max_rev, int(req.form.get('revcount', [25])[0]))
587 revnode = web.repo.changelog.node(rev)
603 revnode = web.repo.changelog.node(rev)
588 revnode_hex = hex(revnode)
604 revnode_hex = hex(revnode)
589 uprev = min(max_rev, rev + revcount)
605 uprev = min(max_rev, rev + revcount)
590 downrev = max(0, rev - revcount)
606 downrev = max(0, rev - revcount)
591 lessrev = max(0, rev - revcount / 2)
607 lessrev = max(0, rev - revcount / 2)
592
608
593 maxchanges = web.maxshortchanges or web.maxchanges
609 maxchanges = web.maxshortchanges or web.maxchanges
594 count = len(web.repo)
610 count = len(web.repo)
595 changenav = webutil.revnavgen(rev, maxchanges, count, web.repo.changectx)
611 changenav = webutil.revnavgen(rev, maxchanges, count, web.repo.changectx)
596
612
597 tree = list(graphmod.graph(web.repo, rev, downrev))
613 tree = list(graphmod.graph(web.repo, rev, downrev))
598 canvasheight = (len(tree) + 1) * bg_height - 27;
614 canvasheight = (len(tree) + 1) * bg_height - 27;
599
615
600 data = []
616 data = []
601 for i, (ctx, vtx, edges) in enumerate(tree):
617 for i, (ctx, vtx, edges) in enumerate(tree):
602 node = short(ctx.node())
618 node = short(ctx.node())
603 age = templatefilters.age(ctx.date())
619 age = templatefilters.age(ctx.date())
604 desc = templatefilters.firstline(ctx.description())
620 desc = templatefilters.firstline(ctx.description())
605 desc = cgi.escape(desc)
621 desc = cgi.escape(desc)
606 user = cgi.escape(templatefilters.person(ctx.user()))
622 user = cgi.escape(templatefilters.person(ctx.user()))
607 branch = ctx.branch()
623 branch = ctx.branch()
608 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
624 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
609 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
625 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
610
626
611 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
627 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
612 lessrev=lessrev, revcountmore=revcount and 2 * revcount or 1,
628 lessrev=lessrev, revcountmore=revcount and 2 * revcount or 1,
613 revcountless=revcount / 2, downrev=downrev,
629 revcountless=revcount / 2, downrev=downrev,
614 canvasheight=canvasheight, bg_height=bg_height,
630 canvasheight=canvasheight, bg_height=bg_height,
615 jsdata=data, node=revnode_hex, changenav=changenav)
631 jsdata=data, node=revnode_hex, changenav=changenav)
General Comments 0
You need to be logged in to leave comments. Login now