##// END OF EJS Templates
hgweb: use patch.diff() to make sensible diffs (issue1223, issue1258)...
Dirkjan Ochtman -
r7309:e74a9173 default
parent child Browse files
Show More
@@ -0,0 +1,34
1 echo % setting up repo
2 hg init test
3 cd test
4 echo a > a
5 echo b > b
6 hg ci -Ama
7
8 echo % change permissions for git diffs
9 chmod 755 a
10 hg ci -Amb
11
12 echo % set up hgweb
13 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
14 cat hg.pid >> $DAEMON_PIDS
15
16 echo % revision
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0'
18
19 echo % diff removed file
20 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a'
21
22 echo % set up hgweb with git diffs
23 kill `cat hg.pid`
24 hg serve --config 'diff.git=1' -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
25 cat hg.pid >> $DAEMON_PIDS
26
27 echo % revision
28 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0'
29
30 echo % diff removed file
31 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a'
32
33 echo % errors
34 cat errors.log
@@ -0,0 +1,272
1 % setting up repo
2 adding a
3 adding b
4 % change permissions for git diffs
5 % set up hgweb
6 % revision
7 200 Script output follows
8
9 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
10 <html>
11 <head>
12 <link rel="icon" href="/static/hgicon.png" type="image/png">
13 <meta name="robots" content="index, nofollow" />
14 <link rel="stylesheet" href="/static/style.css" type="text/css" />
15
16 <title>test: changeset 0cd96de13884</title>
17 </head>
18 <body>
19
20 <div class="buttons">
21 <a href="/log/0">changelog</a>
22 <a href="/shortlog/0">shortlog</a>
23 <a href="/graph">graph</a>
24 <a href="/tags">tags</a>
25 <a href="/file/0cd96de13884">files</a>
26 <a href="/raw-rev/0cd96de13884">raw</a>
27
28 </div>
29
30 <h2>changeset: a</h2>
31
32 <table id="changesetEntry">
33 <tr>
34 <th class="changeset">changeset 0:</th>
35 <td class="changeset"><a href="/rev/0cd96de13884">0cd96de13884</a></td>
36 </tr>
37
38 <tr><th class="child">child 1:</th><td class="child"><a href="/rev/78e4ebad7cdf">78e4ebad7cdf</a></td></tr>
39
40 <tr>
41 <th class="author">author:</th>
42 <td class="author">&#116;&#101;&#115;&#116;</td>
43 </tr>
44 <tr>
45 <th class="date">date:</th>
46 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (38 years ago)</td>
47 </tr>
48 <tr>
49 <th class="files">files:</th>
50 <td class="files"><a href="/file/0cd96de13884/a">a</a> <a href="/file/0cd96de13884/b">b</a> </td>
51 </tr>
52 <tr>
53 <th class="description">description:</th>
54 <td class="description">a</td>
55 </tr>
56 </table>
57
58 <div id="changesetDiff">
59 <pre class="parity0"><span class="minusline"><a class="lineno" href="#l1" id="l1"> 1</a>--- /dev/null Thu Jan 01 00:00:00 1970 +0000
60 </span><span class="plusline"><a class="lineno" href="#l2" id="l2"> 2</a>+++ b/a Thu Jan 01 00:00:00 1970 +0000
61 </span><span class="atline"><a class="lineno" href="#l3" id="l3"> 3</a>@@ -0,0 +1,1 @@
62 </span><span class="plusline"><a class="lineno" href="#l4" id="l4"> 4</a>+a
63 </span></pre><pre class="parity1"><span class="minusline"><a class="lineno" href="#l1.1" id="l1.1"> 1.1</a>--- /dev/null Thu Jan 01 00:00:00 1970 +0000
64 </span><span class="plusline"><a class="lineno" href="#l1.2" id="l1.2"> 1.2</a>+++ b/b Thu Jan 01 00:00:00 1970 +0000
65 </span><span class="atline"><a class="lineno" href="#l1.3" id="l1.3"> 1.3</a>@@ -0,0 +1,1 @@
66 </span><span class="plusline"><a class="lineno" href="#l1.4" id="l1.4"> 1.4</a>+b
67 </span></pre>
68 </div>
69
70
71 <div class="logo">
72 <a href="http://www.selenic.com/mercurial/">
73 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
74 </div>
75
76 </body>
77 </html>
78
79
80
81 % diff removed file
82 200 Script output follows
83
84 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
85 <html>
86 <head>
87 <link rel="icon" href="/static/hgicon.png" type="image/png">
88 <meta name="robots" content="index, nofollow" />
89 <link rel="stylesheet" href="/static/style.css" type="text/css" />
90
91 <title>test: a diff</title>
92 </head>
93 <body>
94
95 <div class="buttons">
96 <a href="/log/1">changelog</a>
97 <a href="/shortlog/1">shortlog</a>
98 <a href="/graph">graph</a>
99 <a href="/tags">tags</a>
100 <a href="/rev/78e4ebad7cdf">changeset</a>
101 <a href="/file/78e4ebad7cdf/a">file</a>
102 <a href="/log/78e4ebad7cdf/a">revisions</a>
103 <a href="/annotate/78e4ebad7cdf/a">annotate</a>
104 <a href="/raw-diff/78e4ebad7cdf/a">raw</a>
105 </div>
106
107 <h2>a</h2>
108
109 <table id="filediffEntry">
110 <tr>
111 <th class="revision">revision 1:</th>
112 <td class="revision"><a href="/rev/78e4ebad7cdf">78e4ebad7cdf</a></td>
113 </tr>
114
115
116 </table>
117
118 <div id="fileDiff">
119 <pre class="parity0"><span class="minusline"><a class="lineno" href="#l1" id="l1"> 1</a>--- /dev/null Thu Jan 01 00:00:00 1970 +0000
120 </span><span class="plusline"><a class="lineno" href="#l2" id="l2"> 2</a>+++ b/a Thu Jan 01 00:00:00 1970 +0000
121 </span><span class="atline"><a class="lineno" href="#l3" id="l3"> 3</a>@@ -0,0 +1,1 @@
122 </span><span class="plusline"><a class="lineno" href="#l4" id="l4"> 4</a>+a
123 </span></pre>
124 </div>
125
126
127 <div class="logo">
128 <a href="http://www.selenic.com/mercurial/">
129 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
130 </div>
131
132 </body>
133 </html>
134
135
136
137 % set up hgweb with git diffs
138 % revision
139 200 Script output follows
140
141 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
142 <html>
143 <head>
144 <link rel="icon" href="/static/hgicon.png" type="image/png">
145 <meta name="robots" content="index, nofollow" />
146 <link rel="stylesheet" href="/static/style.css" type="text/css" />
147
148 <title>test: changeset 0cd96de13884</title>
149 </head>
150 <body>
151
152 <div class="buttons">
153 <a href="/log/0">changelog</a>
154 <a href="/shortlog/0">shortlog</a>
155 <a href="/graph">graph</a>
156 <a href="/tags">tags</a>
157 <a href="/file/0cd96de13884">files</a>
158 <a href="/raw-rev/0cd96de13884">raw</a>
159
160 </div>
161
162 <h2>changeset: a</h2>
163
164 <table id="changesetEntry">
165 <tr>
166 <th class="changeset">changeset 0:</th>
167 <td class="changeset"><a href="/rev/0cd96de13884">0cd96de13884</a></td>
168 </tr>
169
170 <tr><th class="child">child 1:</th><td class="child"><a href="/rev/78e4ebad7cdf">78e4ebad7cdf</a></td></tr>
171
172 <tr>
173 <th class="author">author:</th>
174 <td class="author">&#116;&#101;&#115;&#116;</td>
175 </tr>
176 <tr>
177 <th class="date">date:</th>
178 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (38 years ago)</td>
179 </tr>
180 <tr>
181 <th class="files">files:</th>
182 <td class="files"><a href="/file/0cd96de13884/a">a</a> <a href="/file/0cd96de13884/b">b</a> </td>
183 </tr>
184 <tr>
185 <th class="description">description:</th>
186 <td class="description">a</td>
187 </tr>
188 </table>
189
190 <div id="changesetDiff">
191 <pre class="parity0"><a class="lineno" href="#l1" id="l1"> 1</a>new file mode 100644
192 <span class="minusline"><a class="lineno" href="#l2" id="l2"> 2</a>--- /dev/null
193 </span><span class="plusline"><a class="lineno" href="#l3" id="l3"> 3</a>+++ b/a
194 </span><span class="atline"><a class="lineno" href="#l4" id="l4"> 4</a>@@ -0,0 +1,1 @@
195 </span><span class="plusline"><a class="lineno" href="#l5" id="l5"> 5</a>+a
196 </span></pre><pre class="parity1"><a class="lineno" href="#l1.1" id="l1.1"> 1.1</a>new file mode 100644
197 <span class="minusline"><a class="lineno" href="#l1.2" id="l1.2"> 1.2</a>--- /dev/null
198 </span><span class="plusline"><a class="lineno" href="#l1.3" id="l1.3"> 1.3</a>+++ b/b
199 </span><span class="atline"><a class="lineno" href="#l1.4" id="l1.4"> 1.4</a>@@ -0,0 +1,1 @@
200 </span><span class="plusline"><a class="lineno" href="#l1.5" id="l1.5"> 1.5</a>+b
201 </span></pre>
202 </div>
203
204
205 <div class="logo">
206 <a href="http://www.selenic.com/mercurial/">
207 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
208 </div>
209
210 </body>
211 </html>
212
213
214
215 % diff removed file
216 200 Script output follows
217
218 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
219 <html>
220 <head>
221 <link rel="icon" href="/static/hgicon.png" type="image/png">
222 <meta name="robots" content="index, nofollow" />
223 <link rel="stylesheet" href="/static/style.css" type="text/css" />
224
225 <title>test: a diff</title>
226 </head>
227 <body>
228
229 <div class="buttons">
230 <a href="/log/1">changelog</a>
231 <a href="/shortlog/1">shortlog</a>
232 <a href="/graph">graph</a>
233 <a href="/tags">tags</a>
234 <a href="/rev/78e4ebad7cdf">changeset</a>
235 <a href="/file/78e4ebad7cdf/a">file</a>
236 <a href="/log/78e4ebad7cdf/a">revisions</a>
237 <a href="/annotate/78e4ebad7cdf/a">annotate</a>
238 <a href="/raw-diff/78e4ebad7cdf/a">raw</a>
239 </div>
240
241 <h2>a</h2>
242
243 <table id="filediffEntry">
244 <tr>
245 <th class="revision">revision 1:</th>
246 <td class="revision"><a href="/rev/78e4ebad7cdf">78e4ebad7cdf</a></td>
247 </tr>
248
249
250 </table>
251
252 <div id="fileDiff">
253 <pre class="parity0"><a class="lineno" href="#l1" id="l1"> 1</a>new file mode 100755
254 <span class="minusline"><a class="lineno" href="#l2" id="l2"> 2</a>--- /dev/null
255 </span><span class="plusline"><a class="lineno" href="#l3" id="l3"> 3</a>+++ b/a
256 </span><span class="atline"><a class="lineno" href="#l4" id="l4"> 4</a>@@ -0,0 +1,1 @@
257 </span><span class="plusline"><a class="lineno" href="#l5" id="l5"> 5</a>+a
258 </span></pre>
259 </div>
260
261
262 <div class="logo">
263 <a href="http://www.selenic.com/mercurial/">
264 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
265 </div>
266
267 </body>
268 </html>
269
270
271
272 % errors
@@ -1,381 +1,356
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os, mimetypes
10 10 from mercurial.node import hex, nullid
11 11 from mercurial.repo import RepoError
12 from mercurial import mdiff, ui, hg, util, patch, hook
12 from mercurial import ui, hg, util, patch, hook, match
13 13 from mercurial import revlog, templater, templatefilters
14 14 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse
15 15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
16 16 from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED
17 17 from request import wsgirequest
18 18 import webcommands, protocol, webutil
19 19
20 20 perms = {
21 21 'changegroup': 'pull',
22 22 'changegroupsubset': 'pull',
23 23 'unbundle': 'push',
24 24 'stream_out': 'pull',
25 25 }
26 26
27 27 class hgweb(object):
28 28 def __init__(self, repo, name=None):
29 29 if isinstance(repo, str):
30 30 parentui = ui.ui(report_untrusted=False, interactive=False)
31 31 self.repo = hg.repository(parentui, repo)
32 32 else:
33 33 self.repo = repo
34 34
35 35 hook.redirect(True)
36 36 self.mtime = -1
37 37 self.reponame = name
38 38 self.archives = 'zip', 'gz', 'bz2'
39 39 self.stripecount = 1
40 40 # a repo owner may set web.templates in .hg/hgrc to get any file
41 41 # readable by the user running the CGI script
42 42 self.templatepath = self.config("web", "templates",
43 43 templater.templatepath(),
44 44 untrusted=False)
45 45
46 46 # The CGI scripts are often run by a user different from the repo owner.
47 47 # Trust the settings from the .hg/hgrc files by default.
48 48 def config(self, section, name, default=None, untrusted=True):
49 49 return self.repo.ui.config(section, name, default,
50 50 untrusted=untrusted)
51 51
52 52 def configbool(self, section, name, default=False, untrusted=True):
53 53 return self.repo.ui.configbool(section, name, default,
54 54 untrusted=untrusted)
55 55
56 56 def configlist(self, section, name, default=None, untrusted=True):
57 57 return self.repo.ui.configlist(section, name, default,
58 58 untrusted=untrusted)
59 59
60 60 def refresh(self):
61 61 mtime = get_mtime(self.repo.root)
62 62 if mtime != self.mtime:
63 63 self.mtime = mtime
64 64 self.repo = hg.repository(self.repo.ui, self.repo.root)
65 65 self.maxchanges = int(self.config("web", "maxchanges", 10))
66 66 self.stripecount = int(self.config("web", "stripes", 1))
67 67 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
68 68 self.maxfiles = int(self.config("web", "maxfiles", 10))
69 69 self.allowpull = self.configbool("web", "allowpull", True)
70 70 self.encoding = self.config("web", "encoding", util._encoding)
71 71
72 72 def run(self):
73 73 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
74 74 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
75 75 import mercurial.hgweb.wsgicgi as wsgicgi
76 76 wsgicgi.launch(self)
77 77
78 78 def __call__(self, env, respond):
79 79 req = wsgirequest(env, respond)
80 80 return self.run_wsgi(req)
81 81
82 82 def run_wsgi(self, req):
83 83
84 84 self.refresh()
85 85
86 86 # process this if it's a protocol request
87 87 # protocol bits don't need to create any URLs
88 88 # and the clients always use the old URL structure
89 89
90 90 cmd = req.form.get('cmd', [''])[0]
91 91 if cmd and cmd in protocol.__all__:
92 92 try:
93 93 if cmd in perms:
94 94 try:
95 95 self.check_perm(req, perms[cmd])
96 96 except ErrorResponse, inst:
97 97 if cmd == 'unbundle':
98 98 req.drain()
99 99 raise
100 100 method = getattr(protocol, cmd)
101 101 return method(self.repo, req)
102 102 except ErrorResponse, inst:
103 103 req.respond(inst.code, protocol.HGTYPE)
104 104 if not inst.message:
105 105 return []
106 106 return '0\n%s\n' % inst.message,
107 107
108 108 # work with CGI variables to create coherent structure
109 109 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
110 110
111 111 req.url = req.env['SCRIPT_NAME']
112 112 if not req.url.endswith('/'):
113 113 req.url += '/'
114 114 if 'REPO_NAME' in req.env:
115 115 req.url += req.env['REPO_NAME'] + '/'
116 116
117 117 if 'PATH_INFO' in req.env:
118 118 parts = req.env['PATH_INFO'].strip('/').split('/')
119 119 repo_parts = req.env.get('REPO_NAME', '').split('/')
120 120 if parts[:len(repo_parts)] == repo_parts:
121 121 parts = parts[len(repo_parts):]
122 122 query = '/'.join(parts)
123 123 else:
124 124 query = req.env['QUERY_STRING'].split('&', 1)[0]
125 125 query = query.split(';', 1)[0]
126 126
127 127 # translate user-visible url structure to internal structure
128 128
129 129 args = query.split('/', 2)
130 130 if 'cmd' not in req.form and args and args[0]:
131 131
132 132 cmd = args.pop(0)
133 133 style = cmd.rfind('-')
134 134 if style != -1:
135 135 req.form['style'] = [cmd[:style]]
136 136 cmd = cmd[style+1:]
137 137
138 138 # avoid accepting e.g. style parameter as command
139 139 if hasattr(webcommands, cmd):
140 140 req.form['cmd'] = [cmd]
141 141 else:
142 142 cmd = ''
143 143
144 144 if cmd == 'static':
145 145 req.form['file'] = ['/'.join(args)]
146 146 else:
147 147 if args and args[0]:
148 148 node = args.pop(0)
149 149 req.form['node'] = [node]
150 150 if args:
151 151 req.form['file'] = args
152 152
153 153 if cmd == 'archive':
154 154 fn = req.form['node'][0]
155 155 for type_, spec in self.archive_specs.iteritems():
156 156 ext = spec[2]
157 157 if fn.endswith(ext):
158 158 req.form['node'] = [fn[:-len(ext)]]
159 159 req.form['type'] = [type_]
160 160
161 161 # process the web interface request
162 162
163 163 try:
164 164
165 165 tmpl = self.templater(req)
166 166 ctype = tmpl('mimetype', encoding=self.encoding)
167 167 ctype = templater.stringify(ctype)
168 168
169 169 if cmd == '':
170 170 req.form['cmd'] = [tmpl.cache['default']]
171 171 cmd = req.form['cmd'][0]
172 172
173 173 if cmd not in webcommands.__all__:
174 174 msg = 'no such method: %s' % cmd
175 175 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
176 176 elif cmd == 'file' and 'raw' in req.form.get('style', []):
177 177 self.ctype = ctype
178 178 content = webcommands.rawfile(self, req, tmpl)
179 179 else:
180 180 content = getattr(webcommands, cmd)(self, req, tmpl)
181 181 req.respond(HTTP_OK, ctype)
182 182
183 183 return ''.join(content),
184 184
185 185 except revlog.LookupError, err:
186 186 req.respond(HTTP_NOT_FOUND, ctype)
187 187 msg = str(err)
188 188 if 'manifest' not in msg:
189 189 msg = 'revision not found: %s' % err.name
190 190 return ''.join(tmpl('error', error=msg)),
191 191 except (RepoError, revlog.RevlogError), inst:
192 192 req.respond(HTTP_SERVER_ERROR, ctype)
193 193 return ''.join(tmpl('error', error=str(inst))),
194 194 except ErrorResponse, inst:
195 195 req.respond(inst.code, ctype)
196 196 return ''.join(tmpl('error', error=inst.message)),
197 197
198 198 def templater(self, req):
199 199
200 200 # determine scheme, port and server name
201 201 # this is needed to create absolute urls
202 202
203 203 proto = req.env.get('wsgi.url_scheme')
204 204 if proto == 'https':
205 205 proto = 'https'
206 206 default_port = "443"
207 207 else:
208 208 proto = 'http'
209 209 default_port = "80"
210 210
211 211 port = req.env["SERVER_PORT"]
212 212 port = port != default_port and (":" + port) or ""
213 213 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
214 214 staticurl = self.config("web", "staticurl") or req.url + 'static/'
215 215 if not staticurl.endswith('/'):
216 216 staticurl += '/'
217 217
218 218 # some functions for the templater
219 219
220 220 def header(**map):
221 221 yield tmpl('header', encoding=self.encoding, **map)
222 222
223 223 def footer(**map):
224 224 yield tmpl("footer", **map)
225 225
226 226 def motd(**map):
227 227 yield self.config("web", "motd", "")
228 228
229 229 def sessionvars(**map):
230 230 fields = []
231 231 if 'style' in req.form:
232 232 style = req.form['style'][0]
233 233 if style != self.config('web', 'style', ''):
234 234 fields.append(('style', style))
235 235
236 236 separator = req.url[-1] == '?' and ';' or '?'
237 237 for name, value in fields:
238 238 yield dict(name=name, value=value, separator=separator)
239 239 separator = ';'
240 240
241 241 # figure out which style to use
242 242
243 243 style = self.config("web", "style", "")
244 244 if 'style' in req.form:
245 245 style = req.form['style'][0]
246 246 mapfile = style_map(self.templatepath, style)
247 247
248 248 if not self.reponame:
249 249 self.reponame = (self.config("web", "name")
250 250 or req.env.get('REPO_NAME')
251 251 or req.url.strip('/') or self.repo.root)
252 252
253 253 # create the templater
254 254
255 255 tmpl = templater.templater(mapfile, templatefilters.filters,
256 256 defaults={"url": req.url,
257 257 "staticurl": staticurl,
258 258 "urlbase": urlbase,
259 259 "repo": self.reponame,
260 260 "header": header,
261 261 "footer": footer,
262 262 "motd": motd,
263 263 "sessionvars": sessionvars
264 264 })
265 265 return tmpl
266 266
267 267 def archivelist(self, nodeid):
268 268 allowed = self.configlist("web", "allow_archive")
269 269 for i, spec in self.archive_specs.iteritems():
270 270 if i in allowed or self.configbool("web", "allow" + i):
271 271 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
272 272
273 273 def listfilediffs(self, tmpl, files, changeset):
274 274 for f in files[:self.maxfiles]:
275 275 yield tmpl("filedifflink", node=hex(changeset), file=f)
276 276 if len(files) > self.maxfiles:
277 277 yield tmpl("fileellipses")
278 278
279 279 def diff(self, tmpl, node1, node2, files):
280 def filterfiles(filters, files):
281 l = [x for x in files if x in filters]
282
283 for t in filters:
284 if t and t[-1] != os.sep:
285 t += os.sep
286 l += [x for x in files if x.startswith(t)]
287 return l
288
289 parity = paritygen(self.stripecount)
290 def diffblock(diff, f, fn):
291 yield tmpl("diffblock",
292 lines=prettyprintlines(diff),
293 parity=parity.next(),
294 file=f,
295 filenode=hex(fn or nullid))
296 280
297 281 blockcount = countgen()
298 282 def prettyprintlines(diff):
299 283 blockno = blockcount.next()
300 for lineno, l in enumerate(diff.splitlines(1)):
284 for lineno, l in enumerate(diff.splitlines(True)):
301 285 if blockno == 0:
302 286 lineno = lineno + 1
303 287 else:
304 288 lineno = "%d.%d" % (blockno, lineno + 1)
305 289 if l.startswith('+'):
306 290 ltype = "difflineplus"
307 291 elif l.startswith('-'):
308 292 ltype = "difflineminus"
309 293 elif l.startswith('@'):
310 294 ltype = "difflineat"
311 295 else:
312 296 ltype = "diffline"
313 297 yield tmpl(ltype,
314 298 line=l,
315 299 lineid="l%s" % lineno,
316 300 linenumber="% 8s" % lineno)
317 301
318 r = self.repo
319 c1 = r[node1]
320 c2 = r[node2]
321 date1 = util.datestr(c1.date())
322 date2 = util.datestr(c2.date())
302 if files:
303 m = match.exact(self.repo.root, self.repo.getcwd(), files)
304 else:
305 m = match.always(self.repo.root, self.repo.getcwd())
323 306
324 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
325 if files:
326 modified, added, removed = map(lambda x: filterfiles(files, x),
327 (modified, added, removed))
328
307 block = []
308 parity = paritygen(self.stripecount)
329 309 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
330 for f in modified:
331 to = c1.filectx(f).data()
332 tn = c2.filectx(f).data()
333 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
334 opts=diffopts), f, tn)
335 for f in added:
336 to = None
337 tn = c2.filectx(f).data()
338 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
339 opts=diffopts), f, tn)
340 for f in removed:
341 to = c1.filectx(f).data()
342 tn = None
343 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, f,
344 opts=diffopts), f, tn)
310 for chunk in patch.diff(self.repo, node1, node2, m, opts=diffopts):
311 if chunk.startswith('diff') and block:
312 yield tmpl('diffblock', parity=parity.next(),
313 lines=prettyprintlines(''.join(block)))
314 block = []
315 if chunk.startswith('diff'):
316 chunk = ''.join(chunk.splitlines(True)[1:])
317 block.append(chunk)
318 yield tmpl('diffblock', parity=parity.next(),
319 lines=prettyprintlines(''.join(block)))
345 320
346 321 archive_specs = {
347 322 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
348 323 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
349 324 'zip': ('application/zip', 'zip', '.zip', None),
350 325 }
351 326
352 327 def check_perm(self, req, op):
353 328 '''Check permission for operation based on request data (including
354 329 authentication info. Return true if op allowed, else false.'''
355 330
356 331 if op == 'pull' and not self.allowpull:
357 332 raise ErrorResponse(HTTP_OK, '')
358 333 elif op == 'pull':
359 334 return
360 335
361 336 # enforce that you can only push using POST requests
362 337 if req.env['REQUEST_METHOD'] != 'POST':
363 338 msg = 'push requires POST request'
364 339 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
365 340
366 341 # require ssl by default for pushing, auth info cannot be sniffed
367 342 # and replayed
368 343 scheme = req.env.get('wsgi.url_scheme')
369 344 if self.configbool('web', 'push_ssl', True) and scheme != 'https':
370 345 raise ErrorResponse(HTTP_OK, 'ssl required')
371 346
372 347 user = req.env.get('REMOTE_USER')
373 348
374 349 deny = self.configlist('web', 'deny_push')
375 350 if deny and (not user or deny == ['*'] or user in deny):
376 351 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
377 352
378 353 allow = self.configlist('web', 'allow_push')
379 354 result = allow and (allow == ['*'] or user in allow)
380 355 if not result:
381 356 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
General Comments 0
You need to be logged in to leave comments. Login now